Email and inbox

Connecting mailboxes, sending, sending limits, deliverability, and how replies are classified and answered.

Mailboxes

ZeroReach sends from your own mailboxes. POST /api/v1/mailboxes/connect starts connecting a Gmail, Outlook or IMAP mailbox and returns the provider's hosted sign-in URL. When you finish, the mailbox appears in GET /api/v1/mailboxes. Provider secrets are encrypted at rest.

A workspace can also bring its own mail provider account; its inbound events arrive at /api/v1/webhooks/mail/{config_id}.

Sending

Email goes out in two ways:

  • Sequence steps, sent by the runner.
  • One-off emails to a lead, with POST /api/v1/mailboxes/{mailbox_id}/send. This is a human-only action: Zero never sends, and with an API key it needs mailboxes:write.

Both paths enforce the same rules in code:

  • The lead's email must not be suppressed, and the contact check must allow it. Otherwise: 409 Sending is not allowed: <reasons>.
  • The mailbox must be connected, not paused by the deliverability guard, and under its daily cap (409).
  • A send held back by contact pressure or a sending limit answers 409 contact_deferred with next_allowed_at.
  • Marketing email carries one-click List-Unsubscribe headers and the CAN-SPAM footer with the workspace's postal address (409 missing_postal_address without one).
  • Every send is logged and added to the lead's timeline (email_sent).

Sending limits

Before each email, the server takes one token from every bucket that covers it. If any bucket is empty, the send is deferred, never dropped.

BucketDefault
Per mailbox, per hour20
Per sending domain, per day300
Per recipient domain, per hour10
Emails per workspace, per minute30
Emails per provider, per minute120

A workspace can only lower these limits. Freemail and reserved domains (such as gmail.com) don't get a sending-domain or recipient-domain bucket. Each mailbox also has a daily cap (daily_send_cap), and the server can ramp a new mailbox up to it gradually (warmup).

Deliverability

GET /api/v1/deliverability scores sender health from 0 to 100, per mailbox and per sending domain. The score covers hard-bounce and complaint rates, pauses, the connection, and the domain's last SPF, DKIM, DMARC and MX check (POST /api/v1/deliverability/domains/{domain}/check).

The deliverability guard pauses a mailbox when bounces or complaints spike, before providers penalise the domain. The rules, over a rolling 7-day window:

  • a hard-bounce rate above 2% once at least 50 emails went out;
  • 10 or more hard bounces, whatever the volume;
  • at least 2 complaints and a complaint rate above 0.1%.

A paused mailbox sends nothing until you resume it (POST /api/v1/mailboxes/{mailbox_id}/resume). The pause also emits a mailbox.paused webhook event.

Replies and the inbox

Inbound replies are classified by the AI into one category: interested, question, objection, referral, out_of_office, not_interested, unsubscribe, bounce, wrong_person or other.

The AI only labels the reply. Every action is decided in code from the category:

CategoryWhat happens
unsubscribe (or an angry reply)The lead is opted out and every in-flight enrollment stops (opted_out).
bounceThe email address is suppressed and every enrollment stops (bounced).
out_of_officeThe return date is stored; enrollments keep running but wait until the next business day after it.
interested, question, objection, referralThe lead moves from new/contacted to replied (never automatically qualified), and the Inbox agent drafts an answer when it is confident enough.
any other replyEnrollments of sequences with stop_on_reply stop (replied).

Automatic replies (detected from the headers) are classified and logged, and nothing else happens.

Answer drafts go to the reply drafts queue (/api/v1/reply-drafts). They are never sent automatically: a signed-in person approves and sends each one. List replies with GET /api/v1/replies and a lead's replies with GET /api/v1/leads/{lead_id}/replies.

Every marketing email carries a signed unsubscribe link (/u/{token}) and a List-Unsubscribe header. Using it records the opt-out in the consent ledger. Links already sent keep working.