@absolutejs/dispatch-resend
v0.6.2
Published
Resend-backed EmailAdapter with credential-safe installed effects, signed evidence, and reference-gated query recovery.
Maintainers
Readme
@absolutejs/dispatch-resend
Resend-backed EmailAdapter for
@absolutejs/dispatch.
Docs: absolutejs.com/documentation/dispatch-overview#adapters
Install
bun add @absolutejs/dispatch @absolutejs/dispatch-resend resendUsage
import { Resend } from "resend";
import { createDispatcher } from "@absolutejs/dispatch";
import { createResendAdapter } from "@absolutejs/dispatch-resend";
const resend = new Resend(process.env.RESEND_KEY!);
const dispatcher = createDispatcher({
email: createResendAdapter({
client: resend,
defaultFrom: "[email protected]",
}),
});
const result = await dispatcher.email({
to: "[email protected]",
subject: "Welcome to Acme",
text: "Click here to verify: ...",
// String metadata becomes Resend tags by default:
metadata: { campaign: "welcome-v2", priority: "high" },
});
console.log(result.id); // Resend's message idAPI
createResendAdapter({
client, // Required — your `new Resend(apiKey)`
defaultFrom?, // Required if your messages don't set `from`
tagsFromMetadata?, // Customize metadata → Resend tags mapping
})tagsFromMetadata
By default the adapter maps every string-valued entry in
message.metadata to a Resend tag. Non-string values (numbers,
booleans, objects) are filtered out — Resend requires string for
both name and value.
Override to customize:
createResendAdapter({
client: resend,
tagsFromMetadata: (metadata) => [
// Always include the tenant
...(typeof metadata.tenant === "string"
? [{ name: "tenant", value: metadata.tenant }]
: []),
// Drop debug-only entries
],
});Error mapping
Resend's { data, error } response shape becomes:
errorset → adapter throws (@absolutejs/dispatchrecords the exception on thedispatch.email.sendspan, bumpsfailedcounter, emitsdispatch.email.failedaudit event).data.idpresent →DispatchResult.id= the Resend message id.data.idmissing →DispatchResult.idisundefined,result.providerstill'resend'.
License
Apache 2.0. Tier B substrate-adjacent under the
AbsoluteJS licensing policy — rides @absolutejs/dispatch (BSL Tier
A) and resend (MIT).
The package also exports resendEffectAdapterDescriptor and
createResendEffectAdapterDriver() for @absolutejs/execution. The driver
accepts resolved credentials only in its execution context, requires the exact
Resend API destination and email.send effect, and forwards the durable effect
idempotency key to Resend. Hosts retain ownership of secret storage and provide
the narrow clientForKey factory at the final provider boundary.
Durable effect webhook evidence
The effect driver adds an abs_effect Resend tag containing the durable effect
ID and declares its complete webhook setup: callback template, Standard
Webhooks headers, supported outbound events, exact secret alias,
last-verified-event health signal, and signed-event replacement rotation.
Hosts can pass the exact raw request
body, Standard Webhooks headers, project tenant ID, and that project's exact
RESEND_WEBHOOK_SECRET to verifyResendEffectWebhook(). It uses Resend's SDK
verifier before returning a normalized EffectEvidenceRecord; recipients,
sender, subject, headers, and the raw payload are never returned for storage.
The descriptor uses webhook-query: signed webhooks remain primary, while
createResendEffectQueryDriver() can retrieve an ambiguous send only when
Execution retained the exact Resend email ID returned by the original call.
The execution driver extracts that bounded reference without retaining the
provider response. The query fallback verifies the retrieved email ID and its
abs_effect tag before returning normalized evidence. If an ambiguous network
call returned no ID, the runtime skips this fallback before resolving the API
key and continues waiting for signed webhook or operator evidence; it never
replays the send.
All supported outbound email.* webhook events confirm that the email.send
effect was accepted by Resend, so the normalized effect outcome is
confirmed_succeeded. Delivery state remains explicit in eventType (for
example email.delivered, email.bounced, or email.failed) and must not be
confused with successful inbox delivery. Inbound email.received, contact,
domain, uncorrelated, and invalidly signed events are rejected.
Webhook signing secrets are host ingress credentials, not provider-send
credentials. Store one under the exact project secret alias
RESEND_WEBHOOK_SECRET; do not place it in an adapter installation or resolve
it during email sends.
