Mailbox Setup
zend.sh is a BYOK (Bring Your Own Keys) platform. You connect your own mailboxes, and zend.sh handles rotation, health monitoring, and deliverability. This guide covers connecting the three supported provider types.
Google Workspace
Prerequisites
- A Google Workspace account (not a free @gmail.com account)
- 2-Step Verification enabled on the account
Generate an app password
- Go to Google Account Security
- Under "2-Step Verification", click App passwords
- Select "Mail" as the app and your device
- Copy the 16-character app password
Connect the mailbox
const account = await zend.accounts.create({
email: 'outreach@yourcompany.com',
provider: 'google',
smtp_host: 'smtp.gmail.com',
smtp_port: 465,
smtp_username: 'outreach@yourcompany.com',
smtp_password: 'xxxx xxxx xxxx xxxx', // app password
imap_host: 'imap.gmail.com',
imap_port: 993,
imap_username: 'outreach@yourcompany.com',
imap_password: 'xxxx xxxx xxxx xxxx', // same app password
})Enable IMAP
IMAP must be enabled for reply detection:
- Open Gmail settings (gear icon)
- Go to Forwarding and POP/IMAP
- Under IMAP Access, select Enable IMAP
- Save changes
Microsoft 365
Microsoft 365 uses OAuth2 for authentication. You need to register an Azure AD application.
Register an Azure AD app
- Go to Azure Portal > App registrations
- Click New registration
- Set a name (e.g., "zend.sh") and register
- Under API permissions, add:
SMTP.Send(for sending)IMAP.AccessAsUser.All(for reply detection)
- Under Certificates & secrets, create a client secret
Connect the mailbox
const account = await zend.accounts.create({
email: 'outreach@yourcompany.com',
provider: 'microsoft',
smtp_host: 'smtp.office365.com',
smtp_port: 587,
smtp_username: 'outreach@yourcompany.com',
smtp_password: 'your-password-or-token',
imap_host: 'outlook.office365.com',
imap_port: 993,
imap_username: 'outreach@yourcompany.com',
imap_password: 'your-password-or-token',
})Custom SMTP/IMAP
Any email provider that supports SMTP and IMAP can be connected: Zoho, Namecheap, Fastmail, self-hosted, etc.
const account = await zend.accounts.create({
email: 'outreach@yourcompany.com',
provider: 'smtp',
smtp_host: 'smtp.yourdomain.com',
smtp_port: 465, // or 587 for STARTTLS
smtp_username: 'outreach@yourcompany.com',
smtp_password: 'your-password',
imap_host: 'imap.yourdomain.com',
imap_port: 993,
imap_username: 'outreach@yourcompany.com',
imap_password: 'your-password',
})Common provider settings:
| Provider | SMTP Host | SMTP Port | IMAP Host | IMAP Port |
|---|---|---|---|---|
| Google Workspace | smtp.gmail.com | 465 | imap.gmail.com | 993 |
| Microsoft 365 | smtp.office365.com | 587 | outlook.office365.com | 993 |
| Zoho Mail | smtp.zoho.com | 465 | imap.zoho.com | 993 |
| Namecheap | mail.privateemail.com | 465 | mail.privateemail.com | 993 |
Validate DNS records
After connecting a mailbox, validate its SPF and DMARC records. DKIM is not checked by this endpoint (it returns dkim: { configured: false, pass: false, checked: false, reason: 'selector_required' } because no DKIM selector is available to it), so verify DKIM separately:
curl -X POST -H "Authorization: Bearer $ZEND_API_KEY" https://app.zend.sh/api/v1/accounts/{id}/validateThe result is wrapped in the standard response envelope, under data:
{
"data": {
"domain": "yourcompany.com",
"spf": {
"pass": false,
"details": "Received-SPF: softfail (zend.sh: domain of transitioning validator@yourcompany.com does not designate 127.0.0.1 as permitted sender) client-ip=127.0.0.1; envelope-from=\"validator@yourcompany.com\"; helo=yourcompany.com;"
},
"dkim": { "configured": false, "pass": false, "checked": false, "reason": "selector_required" },
"dmarc": { "configured": true, "policy": "reject", "pass": false }
},
"assessment": null,
"insights": [],
"recommendations": [],
"warnings": [],
"next_actions": []
}Read the fields from data, for example jq .data.spf.details or jq .data.dmarc.policy.
This is what a correctly configured domain returns today, here with an SPF record ending in ~all and a p=reject DMARC record. The check evaluates SPF from 127.0.0.1, not from your provider's sending IP, so spf.pass and dmarc.pass are false even when your records are correct. Do not read pass: false as a misconfiguration on its own. Read these instead:
spf.detailsstarts withReceived-SPF: softfail(orfailfor a record ending in-all) when your SPF record was found.nonemeans no SPF record, andpermerrormeans the record is broken, for example two SPF records on the domain.dmarc.configuredistrueanddmarc.policyis your policy when your DMARC record was found.
An SPF TXT lookup failure (for example SERVFAIL) returns 502. A DMARC discovery failure returns 502 when no usable policy was found; DMARC temperror with a discovered policy still returns 200 with that policy. SPF permerror and missing SPF/DMARC records also return 200 verdicts.
The 502 response body has this shape (the SPF or DMARC message includes your domain):
{
"type": "validation_failed",
"message": "Domain validation failed: SPF discovery failed temporarily for yourcompany.com",
"status": 502
}For an unexpected validation error, the message is the fixed text Domain validation failed: an unexpected error occurred; resolver details and server hostnames are not returned.
Ensure your domain has proper DNS records:
- SPF — Authorizes your mail server to send on behalf of your domain
- DKIM — Digitally signs outgoing emails for authenticity
- DMARC — Tells receiving servers how to handle unauthenticated email
Best practices
- Start with 2-3 accounts — Add more mailboxes as health scores stabilize above 80
- Use dedicated sending domains — Avoid using your primary business domain for cold outreach
- Set realistic daily limits — Start with 20-30 emails per account per day, increase gradually
- Monitor health scores — zend.sh auto-pauses accounts when bounce rates exceed 2% or spam complaints exceed 0.3%
- Warm up new accounts — Send small volumes to engaged contacts before scaling up cold outreach