Migrate from Resend
Step-by-step guide to migrate from Resend to Postflare.
Overview
Postflare is a Resend-compatible email platform built on Cloudflare infrastructure. The API is designed to be a drop-in replacement for Resend, so migration typically requires minimal code changes.
Key advantages:
- Resend SDK compatible — keep the official Resend SDK and point it at
https://api.postflare.dev
Step 1: Create a Postflare Account
Sign up at app.postflare.dev and create an organization.
Step 2: Configure Your Domain
Add your sending domain in the Postflare dashboard. If you already have SPF, DKIM, and DMARC records configured for Resend, you will need to update them to point to Postflare.
- Go to Domains in the dashboard
- Click Add Domain and enter your domain name
- Update your DNS records as shown in the dashboard
- Click Verify to confirm the records are correct
Step 3: Create an API Key
Create a new API key in the Postflare dashboard. Postflare keys start with sk_, not Resend's re_: your Resend key does not carry over.
Step 4: Update Your SDK Configuration
Keep the Resend SDK and change two things: the key, and where the SDK sends. Nothing tells you when the second one is wrong — the SDK sends to https://api.resend.com, which answers.
Node.js / TypeScript
Start your process with RESEND_BASE_URL in its environment:
RESEND_BASE_URL=https://api.postflare.devIt has to be there before the process starts. Before resend 6.12.4 the SDK reads it once, when the module is imported, so setting process.env.RESEND_BASE_URL in code after the import changes nothing. For the same reason the constructor option new Resend(key, { baseUrl }) only works from 6.12.4 on; older versions ignore it without an error.
+ // Set RESEND_BASE_URL=https://api.postflare.dev in the environment before
+ // the process starts. Without it the SDK sends to api.resend.com, silently.
import { Resend } from 'resend';
- const resend = new Resend('re_your_resend_key');
+ const resend = new Resend('sk_your_postflare_key');
// All SDK methods work the same
const { data, error } = await resend.emails.send({
from: 'hello@yourdomain.com',
to: 'user@example.com',
subject: 'Hello from Postflare',
html: '<p>It works!</p>',
});Python
The Python SDK builds every request from resend.api_url:
import resend
- resend.api_key = "re_your_resend_key"
+ resend.api_key = "sk_your_postflare_key"
+ resend.api_url = "https://api.postflare.dev"
params: resend.Emails.SendParams = {
"from": "hello@yourdomain.com",
"to": ["user@example.com"],
"subject": "Hello from Postflare",
"html": "<p>It works!</p>",
}
email = resend.Emails.send(params)cURL
- curl -X POST 'https://api.resend.com/emails' \
+ curl -X POST 'https://api.postflare.dev/emails' \
- -H 'Authorization: Bearer re_your_resend_key' \
+ -H 'Authorization: Bearer sk_your_postflare_key' \
-H 'Content-Type: application/json' \
-d '{
"from": "hello@yourdomain.com",
"to": "user@example.com",
"subject": "Hello from Postflare",
"html": "<p>It works!</p>"
}'Step 5: Migrate Webhooks
If you use webhooks, create new webhook endpoints in Postflare. The webhook event format is compatible with Resend.
Postflare also supports additional event types not available in Resend:
| Event Type | Description |
|---|---|
domain.created | Domain was added |
domain.updated | Domain status changed |
domain.deleted | Domain was removed |
contact.created | Contact was created |
contact.updated | Contact was updated |
contact.deleted | Contact was deleted |
Step 6: Test
Send a test email to verify everything works:
const { data, error } = await resend.emails.send({
from: 'hello@yourdomain.com',
to: 'test@example.com',
subject: 'Migration Test',
html: '<p>Postflare is working!</p>',
});
if (error) {
console.error('Error:', error);
} else {
console.log('Email sent:', data.id);
}API Compatibility Reference
The following table lists all Postflare API endpoints and their Resend compatibility status.
Emails
| Endpoint | Method | Resend Compatible |
|---|---|---|
/emails | POST | Compatible |
/emails/batch | POST | Compatible |
/emails | GET | Compatible |
/emails/{id} | GET | Compatible |
/emails/{id} | PATCH | Compatible |
/emails/{id}/cancel | POST | Compatible |
Domains
| Endpoint | Method | Resend Compatible |
|---|---|---|
/domains | POST | Compatible |
/domains | GET | Compatible |
/domains/{id} | GET | Compatible |
/domains/{id}/verify | POST | Compatible |
/domains/{id} | DELETE | Compatible |
Contacts
| Endpoint | Method | Resend Compatible |
|---|---|---|
/contacts | POST | Partial -- adds properties, segments, topics fields |
/contacts | GET | Partial -- organization-level instead of audience-scoped |
/contacts/{id} | GET | Partial -- supports lookup by email address |
/contacts/{id} | PATCH | Partial -- adds properties, topics fields |
/contacts/{id} | DELETE | Partial -- supports lookup by email address |
/audiences/{audience_id}/contacts | POST | Legacy -- creates the contact in that segment |
/audiences/{audience_id}/contacts/{id} | GET, PATCH, DELETE | Legacy -- same as /contacts/{id} |
Contacts belong to the organization, and an audience is what Resend now calls
a segment. The SDK marks the audienceId argument deprecated; prefer
segments on create and segmentId on list. Code that still passes it
keeps working: on create, get, update or remove the SDK requests
/audiences/{audience_id}/contacts, where create adds the new contact to
that segment and the other three act on the contact named by ID or email,
whichever audience you pass. On list, the SDK requests
/segments/{audience_id}/contacts: the contacts of the segment with that ID,
not all of your contacts. On broadcasts.create and broadcasts.update,
audienceId is read as segmentId.
API Keys
| Endpoint | Method | Resend Compatible |
|---|---|---|
/api-keys | POST | Compatible |
/api-keys | GET | Compatible |
/api-keys/{id} | DELETE | Compatible |
Webhooks
| Endpoint | Method | Resend Compatible |
|---|---|---|
/webhooks | POST | Partial -- additional event types |
/webhooks | GET | Compatible |
/webhooks/{id} | GET | Compatible |
/webhooks/{id} | PATCH | Compatible |
/webhooks/{id} | DELETE | Compatible |
Additional Postflare Endpoints
Postflare includes additional endpoints not available in Resend:
| Endpoint | Method | Description |
|---|---|---|
/segments | POST/GET | Segment management |
/segments/{id} | GET/DELETE | Segment operations |
/topics | POST/GET | Topic management |
/topics/{id} | GET/PATCH/DELETE | Topic operations |
/contacts/{id}/segments | GET/POST/DELETE | Contact segment membership |
/contacts/{id}/topics | GET/PATCH | Contact topic subscriptions |
/broadcasts | POST/GET | Broadcast management |
/broadcasts/{id} | GET/PATCH/DELETE | Broadcast operations |
/broadcasts/{id}/send | POST | Send a broadcast |
/templates | POST/GET | Template management |
/templates/{id} | GET/PATCH/DELETE | Template operations |
/templates/{id}/publish | POST | Publish a template |
/templates/{id}/duplicate | POST | Duplicate a template |