# Move a project from Resend

> Point the resend SDK at Minket with two environment variables, then move a project one step at a time with no downtime and a rollback that is one revert.

Source: https://docs.minket.com/migrate/
Updated: 2026-10-29

Minket mirrors Resend's API at the root of `https://api.minket.com`, so code on the official `resend` SDK keeps working once it points here. The [Resend compatibility reference](https://docs.minket.com/compatibility/) lists every mirrored call, the SDK versions it is tested with and each difference.

## Point the SDK at Minket

The SDK reads `RESEND_BASE_URL` and `RESEND_API_KEY` from the environment. Set them and the code stays as it is:

```env
RESEND_BASE_URL=https://api.minket.com
RESEND_API_KEY=minket_live_…
```

When the SDK reads the variable depends on its version. 6.10.0 reads `RESEND_BASE_URL` once, when the module is imported, and has no `baseUrl` option: the variable must be in the environment before the first `import` of `resend`, and changing it later has no effect. 6.12.4 and later read it each time a client is built and also take `new Resend(key, { baseUrl })`. A runtime without `process.env` when the SDK loads (some edge and worker runtimes) silently keeps `https://api.resend.com`; there, pass `baseUrl`, which needs 6.12.4 or later.

Code that calls Resend over plain `fetch` with the host written out needs an edit at each call site: read the host from the same variable. A call site that is missed keeps working against Resend until its domain is removed there, so it fails only weeks later; search the code for `api.resend.com` before the switch. Send `Content-Type: application/json` with the body, which `fetch` leaves out for a string body; Minket refuses a body without it rather than reading it as empty.

```js
const base = process.env.RESEND_BASE_URL ?? "https://api.resend.com";
const response = await fetch(`${base}/emails`, {
  method: "POST",
  headers: {
    Authorization: `Bearer ${process.env.RESEND_API_KEY}`,
    "Content-Type": "application/json",
  },
  body: JSON.stringify(email),
});
```

Webhook endpoints get a new signing secret when they are created here. Put it where the Resend secret was; the verification code does not change.

## Move a project

One project at a time, with no downtime:

1. Add the domain in Minket while it stays in Resend. The DKIM selectors differ and the MAIL FROM is `bounce.`, so both sets of records are valid at once.
2. Add DMARC where it is missing (`p=none`, reports to `dmarc@<receiving domain>`).
3. Preview first: set `RESEND_BASE_URL`, a preview key and the new webhook secret, and test every email path, the raw `fetch` call sites included. Use a test key where the project had its own sandbox.
4. Confirm the preview sends reached Minket: `GET /logs` with the preview key lists each one, and so do the console's Logs and Emails pages. A `minket_` key sent to Resend fails with "API key is invalid"; a send that succeeds but does not show up here went to Resend with the old key, because the SDK did not pick up `RESEND_BASE_URL` or a call site still names `api.resend.com`.
5. Swap the production variables, then check a real send, a webhook delivery and inbound mail where the project uses it, the same way. Rolling back during the following week is reverting the variables; Resend's records are still in place.
6. SMTP users (Supabase auth, for one) point at the SMTP endpoint with a credential from the console ([Send over SMTP](https://docs.minket.com/smtp/)).
7. Inbound domains: lower the MX TTL a day ahead, then switch the MX to the receiving record.
8. Seed suppressions from the app's own tables and from Resend where it exports them: `POST /v1/suppressions` with `reason: "imported"`.
9. After the watch, remove Resend's DNS records and the domain from Resend. Any call site still on `api.resend.com` stops working here.

## Move to the native API later

Both layers share keys, emails, domains and webhooks, so a project can move one call at a time to the native API at `/v1` and the [Minket SDK](https://docs.minket.com/sdks/).
