boundry.
Sydney, Australia
Documentation/migrate from resend

Migrate from Resend

Move outbound transactional email to Boundry in five steps, while keeping Resend available for rollback.

Use the SDK for the application integration. Install boundry-sdk, then keep Resend live until the Boundry validation steps pass.

Before you start

  • List the Resend sends, domains, webhooks and retry rules your application actually uses.
  • Treat broadcasts, contacts, audiences, inbound email and automations as separate migrations.
  • Create separate Boundry staging and production projects in Sydney.

1. Prepare Boundry

  1. Add and verify the sending domain.
  2. Create a project API key and store it only on the server.
  3. Create a webhook endpoint and save its one-time signing secret.
.env
bash
EMAIL_PROVIDER=resend
BOUNDRY_API_KEY=bnd_...
BOUNDRY_WEBHOOK_SECRET=whsec_...

Keep the Resend key and DNS records active throughout validation and rollback.

2. Replace the send call

Install the Boundry SDK, then replace the provider client. The message body barely changes.

Install
bash
npm install boundry-sdk
send.ts
TypeScript
-import { Resend } from "resend";-const resend = new Resend(process.env.RESEND_API_KEY);+import { Boundry } from "boundry-sdk";+const boundry = new Boundry({+  apiKey: process.env.BOUNDRY_API_KEY!,+  region: "au",+}); -const { data, error } = await resend.emails.send({+const { id } = await boundry.emails.send({   from: "Acme <hello@mail.example.com>",   to: ["customer@example.net"],   subject: "Your receipt",   html: "<p>Thanks for your order.</p>",   text: "Thanks for your order.",+}, {+  idempotencyKey: "order:2847:receipt:v1", }); -if (error) throw error;-console.log(data.id);+console.log(id);

Render React Email components to HTML first. Map reply-to and scheduling fields to reply_to and scheduled_at. Derive the idempotency key from the business operation and reuse it after a timeout.

3. Update webhooks

Create a new Boundry endpoint and let the SDK verify the raw request body before you process the event.

route.ts
TypeScript
import { webhooks } from "boundry-sdk";

export async function POST(request: Request) {
  const event = await webhooks.verify(
    await request.arrayBuffer(),
    request.headers,
    process.env.BOUNDRY_WEBHOOK_SECRET!,
  );

  await queueEvent(event);
  return new Response("ok");
}

The verifier checks the signature and replay window. Deduplicate by event.id, enqueue work, and return a 2xx quickly. Test with a real Boundry event before cutover.

4. Validate without duplicates

  • Leave EMAIL_PROVIDER=resend while validating the transformed Boundry payload without sending it.
  • Send Boundry canaries only to company-controlled inboxes.
  • Force a timeout and confirm that retrying with the same idempotency key creates no duplicate.
  • Confirm SPF, DKIM, delivery, bounce and webhook processing in the dashboard.

5. Cut over gradually

1

Start with one low-risk template. Keep each business operation pinned to one provider.

2

Increase traffic in stages. Move through 10%, 50% and 100% with an observation window at each step.

3

Watch delivery and operations. Monitor errors, latency, bounces, complaints, webhook lag and quota headroom.

To roll back, route only new operations to Resend. For any Boundry timeout, inspect its message state before resending elsewhere—the original request may already have been accepted.

Ready to switch

  • The domain is verified and every From address works.
  • Retries do not create duplicates.
  • Webhook signatures and deduplication pass.
  • Delivery, bounce and complaint flows reach their owners.
  • The rollback has been rehearsed.
Prepare the sending domain Read the send-email endpoint