Access is invite-only during beta. Once invited, create a key to personalize these examples. Production sending requires a separate review.
Switching from Resend
Keep the same core send fields while moving authentication, delivery events, and inbound mail to PostShiba.
from, to, subject, and html stay unchanged. Replace the endpoint and re_… key, then migrate webhooks and receiving.
Run both providers during migration
Resend and PostShiba can coexist during outbound cutover when their DKIM selectors and return-path hostnames do not collide. If they collide, use a separate PostShiba sending subdomain. Route each production message through one provider. Never send duplicates.
PostShiba uses shared IPs. This ramp validates requests, idempotency, webhooks, suppressions, attachments, and steady traffic. It is not dedicated-IP warming.
Choose the provider once for each message and keep that choice on retries. Use a deterministic cohort based on a stable message or tenant ID, or feature-flag one message class.
Adjust this 30-day example for your volume and risk. Advance only while request, delivery, bounce, suppression, and webhook checks stay healthy:
- On days 1 to 3, run controlled tests.
- Around day 4, route about 10% of new messages to PostShiba.
- Around day 10, increase to about 25%.
- Around day 20, increase to about 50%.
- By day 30, route 100% of new messages to PostShiba.
Inbound moves separately because MX records cannot split traffic by percentage. Test a hosted inbox or new subdomain before switching MX.
Inventory the Resend integration
Record these items before production:
- Teams,
re_…keys, verified domains, and sending subdomains. - HTTP and SMTP callers, including idempotency, tags, attachments, and templates.
- Webhook URLs, secrets, event branches, and open or click dependencies.
- Receiving domains, MX records, routes, attachment jobs, and any audience or broadcast workflow.
Keep Resend API keys, webhook receivers, and DNS records available for rollback.
Map Resend to PostShiba
| Resend | PostShiba |
|---|---|
Team and re_… API key |
Team and team-locked API key |
| Sending service | Shared cluster |
| Verified domain | Sending domain |
| Separate customer teams or keys | Tenant for resource and suppression isolation |
| Email tags | unique_args |
Svix webhook with email.* events |
HMAC-signed SendGrid-shaped event array |
| Receiving domain and Receiving API | Inbox with a webhook or polling |
| Resend SMTP login | Cluster SMTP credential |
| Audiences and broadcasts | Application-owned contact, campaign, and scheduling code |
| Templates | Hosted Liquid templates. Resend typed variables ({{{FIRST_NAME}}}) become Liquid ({{ name }}). Publish before send. |
Set up PostShiba in order
- Get production approval. A pending team can create one shared cluster, then request approval from the dashboard banner. Wait before cutting over.
- Wait for the shared cluster to become
sending_ready, then issue an SMTP credential. HTTP sends also need this credential. - Create an API key. Confirm it with
GET /api/v1/users/me, then store it server-side. - Add and verify the sending domain. Publish its DKIM and return-path CNAMEs. Keep both providers' non-conflicting records during validation.
- If separate teams or keys isolate customers, create a tenant before its resources. Tenants isolate resources, not contact lists. Single senders can use
default.
Bring your suppressions
Import bounced, complained, and unsubscribed addresses before you send. A missed row is a complaint on the new cluster.
Export those addresses from your application or Resend audience. If separate teams isolate customers, import each list under the matching tenant.
Upload the CSV on Suppressions, or post the addresses:
Use a column named email, address, or recipient, or one address per line. Existing rows are skipped. Max 10,000 addresses per request. See Suppressions.
Switch one HTTP send
Use cluster-specific POST /sends when retries need idempotency. The four core fields stay the same:
Resend accepts Idempotency-Key on POST /emails. PostShiba accepts it only on cluster-specific POST /sends. POST /api/v1/emails chooses a ready cluster but does not support this header.
Move Resend tags into unique_args. PostShiba keeps up to 32 scalar values and drops objects or arrays. Values return inside unique_args and as top-level event fields.
For attachments, send filename, base64 content, and optional content_type. Resend's path and content_id do not map. Fetch files first, or use SMTP or HTTP inject for MIME and inline parts.
Port Resend templates to PostShiba templates. template.id is the alias or public id. template.variables is a Liquid assign hash, not Resend's typed variables array. There is no {{{VAR}}} syntax. Drafts cannot send. from, subject, and reply_to on the request still override the template. SMTP and HTTP inject still take a full MIME body. Move audience and broadcast contact, consent, scheduling, and unsubscribe work to your application or campaign system.
Replace SMTP settings
Replace Resend's SMTP login with these PostShiba SMTP settings:
- Host
smtp.postshiba.com - Port
587with STARTTLS - Authentication
AUTH PLAIN - The issued
smtp_…username and its separate password
Add X-Capsule-Unique-Args as a JSON object when SMTP messages need tracking fields. AUTH LOGIN does not work.
Replace delivery webhooks
Create a webhook with a new secret. Dispatch each object in its JSON array by event, and deduplicate with sg_event_id.
Translate email.sent to processed, email.delivered to delivered, email.delivery_delayed to deferred, email.bounced to bounce, and email.complained to spamreport. Handle dropped and unsubscribe too. PostShiba does not emit open or click events. Remove those dependencies or use another analytics source. Read send errors such as suppressed from the HTTP response.
Replace the Svix headers with X-Capsule-Timestamp and X-Capsule-Signature. Verify the sha256= HMAC-SHA256 over {timestamp}.{raw_body} before parsing the event array. See webhook verification for the exact check.
Move receiving and attachments
Create an inbox for each address. For catch-all receiving, use local_part: "*" on a domain-bound inbox. Hosted addresses use inbound.postshiba.com. Domain-bound addresses use local_part@inbound.{sending_domain}.
Record the old MX and lower its TTL. Do not publish equal-priority provider records. Publish PostShiba MX only on inbound.{sending_domain}, never on the sending domain. Replace any .resend.app public address or Reply-To.
The default receiving webhook uses the same HMAC headers. Its JSON includes text, HTML, headers, the envelope, authentication, and attachments[].content_base64. This replaces Resend's metadata event and follow-up calls. When polling, use message show or the attachment route for bytes.
Validate, cut over, and roll back
- Add
sandbox: trueto the exact/sendsbody. Expect200,queued: false, and amessage_id. - Send live mail only to controlled addresses. Confirm
201, inbox placement, delivery events, signatures,unique_args, retries, suppressions, SMTP, templates, and attachments. - Follow the outbound schedule above. Hold a stage when a check leaves its accepted range.
- Cut inbound MX over separately, then confirm the new inbox receives text, HTML, and an attachment.
For outbound rollback, route only new jobs to Resend. Reconcile accepted PostShiba message_id values before replaying work. For inbound rollback, restore the recorded Resend MX values and wait for the lowered TTL. Keep both webhook receivers available while DNS caches expire.
Final checklist
- Production approval is complete and the shared cluster is
sending_ready. - The token, verified domain, and optional tenants are in place.
- Retry callers use
/sends, not/api/v1/emails. - Tags, attachments, templates, and broadcasts have replacements.
- Both webhook types verify PostShiba signatures over raw bodies.
- SMTP uses STARTTLS on port 587 with
AUTH PLAIN. - Bounced, complained, and unsubscribed addresses are imported.
- Outbound and inbound tests pass without dual-sending production messages.
- Resend routing, API keys, webhook receivers, and DNS records remain available for rollback.