convex-emails

Provider adapters

Compare Plunk and Resend behavior without hiding their differences.

An adapter maps the common message to a provider request. The core persists its name and route with the job. The route identifies one provider account. Keep that identity stable across deployments.

CapabilityPlunkResend
Endpointhttps://next-api.useplunk.com/v1/sendhttps://api.resend.com/emails
Host API keyPLUNK_API_KEYRESEND_API_KEY
HTMLYesYes
Explicit plain textRejectedSupported
HTTP 429Bounded retryBounded retry
Timeout, network failure, HTTP 5xxStop as uncertainRetry with the same key within the budget
HTTP 409UncertainRetry only concurrent_idempotent_requests; otherwise uncertain
Webhook authenticationWorkflow bearer secretSvix signature and timestamp
Subscription mutation on sendNoneNone

Plunk maps html to body and replyTo to reply. Resend maps replyTo to reply_to and sends to as a one-item array. Each request has a 20-second timeout and rejects redirects.

Select an adapter

import { Emails } from "@clipin/convex-emails";
import { plunk } from "@clipin/convex-emails/plunk";
import { resend } from "@clipin/convex-emails/resend";

const primary = new Emails(components.emails, {
	adapter: plunk({ route: "plunk-primary" }),
});
const secondary = new Emails(components.emails, {
	adapter: resend({ route: "resend-primary" }),
});

Export a worker and webhook for each instance. Both can share one component mount. Restrictions apply across that mount. Independent mounts have independent restrictions and pacing.

Custom adapters

The public Adapter type defines name, route, retryWindowMs, capabilities.text, send, and verifyWebhook. send returns a discriminated Outcome: accepted, retry, failed, or uncertain.

A retry includes delayMs and ambiguous. Set ambiguous: true if an earlier request may have been accepted. Never return a retry for an ambiguous request unless the provider supports safe replay with the supplied key. Use WebhookError to return an authentication status from verifyWebhook.

Custom adapters run as trusted host code. The component cannot enforce the truth of their safety claims. There is no automatic cross-provider failover.

On this page