Create a key to copy examples filled with your team.
Build an ESP on PostShiba
Give each customer their own domains, credentials, sending data, and controls.
PostShiba is a good fit when your product sends on behalf of other businesses. You keep the customer-facing API and dashboard. PostShiba handles domain verification, SMTP credentials, message injection, delivery events, bounces, and suppressions.
Within a PostShiba team, use a tenant to group each customer's domains, SMTP credentials, inboxes, and suppressions.
A sensible account model
Use this mapping:
| Your product | PostShiba |
|---|---|
| Your company | One PostShiba team |
| Sending environment | One shared-IP cluster |
| Customer or workspace | One tenant |
| Customer sending domain | A domain owned by that tenant |
| Customer SMTP login | A credential owned by that tenant |
| Delivery stream | Team webhook, routed by tenant slug |
| Do-not-send list | Tenant suppressions |
Start with one cluster unless you have a real operational reason to separate traffic. Customer-created clusters use the shared IP pool. A tenant already gives you the isolation most ESP products need.
Your platform application token can access the whole PostShiba team. Never put it in customer code or a browser. If customers need direct SMTP, give each one a tenant-scoped SMTP credential instead.
1. Prepare the team
Finish PostShiba's sender review, then create a cluster. You only do this once for the sending environment.
Save the returned cluster ID and endpoints. Do not accept production sends until sending_ready is true.
The shell examples share these variables. Run the exports in your current shell, or move them to setup.sh and load it with source setup.sh.
Create one delivery webhook for the team:
Store the returned signing secret. PostShiba sends a JSON array in the SendGrid Event Webhook shape. Authenticated sends normally include the PostShiba tenant slug as tenant_id. Keep your own routing ID in unique_args and alert when an event unexpectedly has no tenant.
2. Provision a customer
Provisioning has three API calls:
- Create a tenant.
- Create a sending domain under that tenant.
- Create an SMTP credential under that tenant.
This small client keeps the sequence in one place:
Call it from your onboarding job:
The REST API returns the SMTP password on credential creation. Do not write it to application logs. If your product offers SMTP, move it straight into encrypted secret storage until the customer retrieves it. Authorized PostShiba dashboard users can currently reveal the encrypted password again. If all mail goes through your own REST API, your application does not need to keep a copy. PostShiba keeps the credential it needs for injection.
Use explicit customer slugs. They appear in webhooks and make support work much easier than a generated numeric ID alone.
3. Guide the customer through DNS
Show the three DNS values from the domain response in your own dashboard:
| Record | Required | Purpose |
|---|---|---|
| DKIM CNAME | Yes | Signs mail for the customer's domain |
| Return-path CNAME | Yes | Routes bounces back to PostShiba |
| SPF TXT include | No | Optional SPF record shown by PostShiba |
Do not invent or transform these values. DNS dashboards vary in how they display the host, so give the customer a copy button for both the host and target.
When the customer says the records are ready, ask PostShiba to check them:
Read the domain until the required states are verified:
Only mark the domain ready in your product when both dkim_status and return_path_status are verified. SPF is reported separately and does not block sending.
PostShiba allows mail from any address at the verified domain. Your product can add its own sender-name or local-part policy if the customer should only use approved addresses.
4. Send for the customer
Your API should authenticate the customer, look up the PostShiba tenant on your server, and call PostShiba with your platform token.
Pass the tenant slug on every send. When the slug resolves, PostShiba scopes domain lookup, credential lookup, and suppressions to that customer.
Only pass slugs from your server-side customer mapping. The current send endpoint does not reject an unknown tenant value. An unknown value falls back to team-wide domain and credential lookup plus the default tenant's suppressions. Treat a missing mapping as a hard failure before you call PostShiba.
Store your transmission_id with PostShiba's returned message_id. Check queued, not only HTTP 201. queued: true means at least one recipient was accepted by the injector. It does not guarantee that every recipient queued because the REST response omits partial rejection details. Send one recipient per request when you need per-recipient certainty. Delivery is a later event.
One send request queues one message. PostShiba does not provide batch sending, scheduling, hosted templates, or an idempotency key on this endpoint. Your product should own those features if customers need them.
If customers connect over SMTP instead, give them the cluster's smtp_endpoint, the tenant credential, port 587, STARTTLS, and AUTH PLAIN. PostShiba does not support AUTH LOGIN. SMTP AUTH validates the credential and tenant state. PostShiba then validates the envelope MAIL FROM against that tenant's verified domains when it accepts the message.
5. Route delivery events
Verify the exact webhook body before parsing it. Then route each event by tenant_id and your transmission_id.
Add a unique constraint on sg_event_id so retries do not apply an event twice. Store first, return 2xx, then run slower customer notifications in a queue.
PostShiba retries failed deliveries and marks them dead after six failed attempts. Replay dead deliveries from the PostShiba dashboard. Replay is not exposed by the API.
6. Make suppressions part of your product
PostShiba creates a tenant suppression after a hard bounce or spam report. A later send to that address returns 403 {"error":"suppressed"}.
List the customer's suppressions with the team endpoint and filter the returned rows by tenant_id in your application:
Add an address when your customer asks you not to send:
Manual suppressions get the reason manual. The other reasons are bounce and spamreport.
Removing a suppression is a deliberate customer action:
7. Add inbound mail when your product needs it
An ESP does not have to stop at outbound mail. Create an inbox under the customer's tenant when you want to offer replies, support addresses, or agent mailboxes.
This example binds an inbox to the customer's verified sending domain:
Publish the returned MX record on inbound.{sending_domain}, then call POST /api/v1/inboxes/:id/verify. Do not move the sending domain's own MX record to PostShiba.
PostShiba can also issue a hosted address without DNS. Omit sending_domain_id and local_part, and PostShiba generates an address on its inbound zone.
Each inbox has its own signing secret. The inbound webhook is a JSON object, while outbound delivery webhooks are JSON arrays. Store incoming mail in your own database. Retention defaults to 168 hours, but it is configurable. Read retention_hours and each message's expires_at instead of assuming the default.
The support desk guide shows threading and replies. The AI agent inbox guide adds queueing and safety controls.
8. Pause a customer cleanly
Tenant suspension is the fastest kill switch. It stops that tenant's sending credentials, domains, and inboxes without affecting other customers. New inbound mail will be rejected until the tenant resumes.
Resume after you resolve the problem:
Disable a leaked or retired SMTP credential separately:
Deleting a tenant is intentionally strict. PostShiba rejects deletion while it owns any sending domain, SMTP credential, or inbox. Disabled credentials and inboxes still count, and the API does not hard-delete them. The public API also cannot delete sending domains or update and delete webhook endpoints. The current dashboard does not expose every cleanup control, so plan for operator-assisted cleanup.
Before you onboard customers
- Keep a durable mapping between your customer ID and PostShiba tenant ID and slug.
- Serialize provisioning per customer. Repeating create calls can make duplicate resources or hit uniqueness validation.
- Keep the platform application token in one server-side integration service.
- Encrypt SMTP passwords and reveal them once.
- Gate customer sending on your own status plus PostShiba's domain and cluster status.
- Use
unique_argsfor non-secret routing IDs. Values appear on delivery webhooks. - Deduplicate delivery events on
sg_event_id. - Expose suppressions and suspension in your own customer support tools.
- Keep a local audit trail for resource creation, credential issue, suspension, and deletion.