Access is invite-only during beta. Once invited, create a key to personalize these examples. Production sending requires a separate review.

Send emails with Convex

Install the Convex component and call sendEmail from a mutation.

PostShiba is a Convex component. Enqueue mail from a mutation and it commits with your transaction. The component owns delivery after that.

clusterId is required. This client posts to POST /api/v1/teams/:teamId/clusters/:clusterId/sends.

email.ts
1 import { mutation } from "./_generated/server"
2 import { v } from "convex/values"
3 import { PostShiba } from "@postshiba/convex"
4 import { components } from "./_generated/api"
5
6 const postshiba = new PostShiba(components.postshiba, {
7 apiKey: "YOUR_API_KEY",
8 teamId: "KjkAJW",
9 clusterId: "NmQpXr",
10 testMode: false,
11 })
12
13 export const sendWelcome = mutation({
14 args: { to: v.string() },
15 returns: v.string(),
16 handler: async (ctx, args) => {
17 return await postshiba.sendEmail(ctx, {
18 from: "hello@mail.example.com",
19 to: args.to,
20 subject: "Welcome",
21 text: "Glad you are here.",
22 })
23 },
24 })

Installation

install.sh
1 npm install github:postshiba/postshiba-convex

Node 18 or later. Open pull requests on postshiba/sdks.

Register the component in convex/convex.config.ts:

convex.config.ts
1 import { defineApp } from "convex/server"
2 import postshiba from "@postshiba/convex/convex.config"
3
4 const app = defineApp()
5 app.use(postshiba)
6
7 export default app

Options fall back to POSTSHIBA_API_KEY, POSTSHIBA_TEAM_ID, POSTSHIBA_CLUSTER_ID, and POSTSHIBA_WEBHOOK_SECRET. testMode defaults to true. Every recipient must then sit inside testDomains (default inbound.postshiba.com). Set testMode: false to send to real addresses.

Use priority: "bulk" for newsletters. Default is transactional.

Webhooks

Route PostShiba webhooks to the component:

http.ts
1 import { httpRouter } from "convex/server"
2 import { httpAction } from "./_generated/server"
3 import { postshiba } from "./email"
4
5 const http = httpRouter()
6
7 http.route({
8 path: "/postshiba/webhook",
9 method: "POST",
10 handler: httpAction(async (ctx, req) => postshiba.handleEventWebhook(ctx, req)),
11 })
12
13 export default http

HMAC-SHA256 of {timestamp}.{rawBody} against X-Capsule-Signature with a sha256= prefix. X-Capsule-Timestamp must be within 300 seconds.

Errors and throttling

Failed responses throw PostShibaHttpError with status, code, and retryable.

A 429 with error throttled means the cluster hit its hourly throttle or its 24-hour cap. This component retries with backoff because it owns the queue. Set hourlyLimit under your cluster hourly throttle.

See Errors.

About

PostShiba is the email platform that powers Bento behind the scenes. You can build your own products, like Bento, on top of it.

© 2026 PostShiba by Backpack Internet Pty. Ltd. All rights reserved.

The same policies that govern Bento are applied to PostShiba Privacy | Terms | Security