Access is invite-only during beta. Once invited, create a key to personalize these examples. Production sending requires a separate review.
Switching from Mailgun
Migrate Mailgun sending, metadata, delivery webhooks, and inbound Routes to PostShiba with a tested rollback path.
Keep sender addresses, content, and HTTP or SMTP clients. Authentication, credentials, requests, webhook signatures, and inbound routing change. Keep Mailgun ready until the rollback window closes.
Run both providers during the outbound migration
Mailgun and PostShiba can run together. Use a feature flag keyed by tenant or sender. Each production message must use exactly one provider. Do not dual-send. PostShiba uses shared IPs, so this is an integration ramp, not dedicated-IP warming.
Start with this adjustable 30-day ramp:
- Days 1 to 3: run controlled tests. Pending accounts may send only to team members or live inboxes. After approval, test external mailboxes.
- Days 4 to 10: route 10% of new outbound traffic to PostShiba.
- Days 11 to 17: increase the cohort to 25%.
- Days 18 to 24: increase the cohort to 50%.
- Days 25 to 30: move to 100% after the 50% stage stays healthy.
Hold a stage when traffic is low. Before each increase, confirm REST or SMTP acceptance, final delivery events, unique_args, webhook verification, and stable bounce, deferral, and complaint rates.
Keep Mailgun credentials, webhooks, Routes, and DNS for rollback. Outbound authentication records can coexist only when their hostnames or selectors differ. If they collide, use a separate PostShiba sending subdomain during the ramp.
Cut inbound receiving over separately. MX records cannot split traffic by percentage, and equal-priority records do not copy messages. Test a hosted inbox or new subdomain before production MX.
Inventory production first
Record these before you change traffic:
- Mailgun domains, regional base URLs, and callers of
POST /v3/{domain}/messages. - Primary or domain sending keys and per-domain SMTP credentials.
- Each
v:variable,X-Mailgun-Variablesheader, tag, delivery webhook, expected event, and signing key. - Inbound recipient domains, MX TTLs, and Route filters, priorities, and actions.
- Mailing-list, stored-template, and sending-IP-pool dependencies.
You need control of sending and receiving DNS, a public HTTPS webhook endpoint, and a PostShiba team.
Map the concepts
| Mailgun | PostShiba |
|---|---|
| Account and API key | Team and API key |
| Domain in the Messages API path | Verified from domain |
| Subaccount | Optional tenant |
| Per-domain SMTP credential | Cluster-and-tenant SMTP credential |
v: variables or X-Mailgun-Variables |
unique_args or X-Capsule-Unique-Args |
| Webhook and signing key | Webhook endpoint and secret |
Route forward() or store() |
Inbox webhook or stored-message polling |
| Lists, stored templates, and IP pools | Hosted Liquid templates for substitution. Lists and IP pools stay application-owned |
Set up PostShiba in order
- Create a shared cluster and wait for
sending_ready: true. Customer IP pools are unsupported. Add extra shared or dedicated sending IPs from Network when you need more. - Request production approval from the dashboard. Pending teams may send only to team members and live inboxes.
- Create an API key. Keep it server-side.
- Map Mailgun subaccounts to tenants before each domain. A tenant scopes resources and suppressions, not Mailgun users, reports, quotas, or billing. Otherwise use the default tenant.
- Add and verify the sending domain. Publish its DKIM and return-path CNAME records.
- Confirm an SMTP credential for each cluster and tenant pair. REST sends also need one.
Bring your suppressions
Import Mailgun bounces, unsubscribes, and complaints before you send. A missed row is a complaint on the new cluster.
Export each list from the Mailgun Suppressions pages, or pull bounces, unsubscribes, and complaints from the Mailgun API. Map subaccount lists to 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.
Replace the Messages API
Mailgun posts form data to a domain URL with Basic api auth. PostShiba posts JSON to a fixed endpoint with a Bearer token. The from domain and optional tenant select the identity.
Send one canary with POST /api/v1/emails:
Replace placeholders and remove tenant for the default tenant. Require HTTP 201 with queued: true, then store message_id. Queued does not mean delivered.
POST /api/v1/emails has no idempotency key. For ambiguous-response retries, use cluster-specific POST /sends with Idempotency-Key, or reconcile the first attempt before retrying.
For SMTP, use the cluster's returned smtp_endpoint on port 587 with STARTTLS and AUTH PLAIN. Issue an smtp_... credential per cluster and tenant. AUTH LOGIN fails. The tenant must own the verified from domain.
Move metadata and unsupported features
Move a Mailgun v:name tracking value to unique_args.name. If it feeds a stored template, port it to a PostShiba template or render text and html in your application. SMTP clients use JSON in X-Capsule-Unique-Args. Limits are 32 scalars, 64-byte keys, and 256-byte values. Do not include secrets.
Move mailing-list membership and consent into your application. If you require an IP pool, stop because PostShiba cannot preserve that behavior. See Network for dedicated egress IPs.
Before the first production cohort, export hard bounces, complaints, and global unsubscribes into the matching tenant suppressions. Do not import temporary failures as permanent suppressions. Keep tag-specific preferences in your application. While both providers run, write new complaints and unsubscribes to the same consent source used by both send paths.
Replace delivery webhooks
Create a PostShiba webhook endpoint and store its secret. The body is a SendGrid-shaped JSON array with processed, delivered, deferred, bounce, dropped, spamreport, or unsubscribe. PostShiba does not emit open or click events.
Map Mailgun events. accepted becomes processed; rejected becomes dropped; temporary and permanent failed become deferred and bounce; complained becomes spamreport; and unsubscribed becomes unsubscribe. delivered stays delivered. Remove dependencies on opened, clicked, and stored.
Give PostShiba its own verifier. Unlike Mailgun's timestamp + token, PostShiba signs {timestamp}.{raw body} with the endpoint secret. Verify X-Capsule-Timestamp and X-Capsule-Signature before parsing.
Replace inbound Routes
Create a PostShiba inbox per local part, or use a domain catch-all. Move Route logic into your application. Choose a signed receiving webhook, or poll stored messages before expires_at.
A domain-bound inbox receives at {local_part}@inbound.{sending_domain}. Publish MX only on that host. If the Mailgun host differs, test a hosted inbox or new subdomain, then update addresses or aliases.
Never use equal-priority Mailgun and PostShiba MX records to split traffic. Sending servers choose one target rather than copying the message, so delivery becomes unpredictable.
Validate, cut over, and roll back
Follow the outbound ramp above. Test inbound signatures, bodies, attachments, and polling on a separate inbox. Lower the host's TTL one old TTL in advance. If Mailgun already receives on the exact PostShiba inbound hostname, replace that MX with the inbox target. Otherwise keep the Mailgun MX and move Reply-To values or aliases to the new PostShiba address. Keep old Routes active for late deliveries.
To roll back outbound, send only new messages through Mailgun. Do not replay a request that returned queued: true without delivery evidence. Keep both event receivers running.
To roll back inbound, restore Mailgun as the only preferred MX target. Keep both inbound handlers alive through the old TTL for cached senders.
Cutover checklist
- Production approval is complete.
- The cluster is sending-ready, and each tenant has a credential.
- DKIM and return-path verification pass.
- Canary sends return
queued: trueand final delivery events. -
unique_argsreach the webhook. - Hard bounces, complaints, and global unsubscribes are present in the matching tenant.
- Templates, lists, and IP-pool dependencies have replacements.
- Inbound webhook or polling works before the MX change.
- Bounce, unsubscribe, and complaint lists are imported per tenant.
- Mailgun credentials, webhooks, Routes, and DNS remain available for rollback.