Sending and quotas

How replies and cold sends are classified, and what happens when a send is refused.

Every outbound message is classified. A reply means the recipient wrote to you first in that thread. A cold send is anything else: it carries an unsubscribe link and is watched by the duplicate-content and velocity tripwires. Both draw down one daily allowance, 100 a day on Free and uncapped on paid plans, and one monthly allowance of 3,000, 10,000, or 150,000. Free stops at its monthly number; paid plans keep sending and meter what goes past it, up to the spend cap if one is set. Both are capped at 5 recipients. Nothing sends at all until the workspace email is verified, which is refused as sending_locked_verify_email.

When a send is rejected, the reason tells the agent what to do: daily_send_quota_exhausted and monthly_send_quota_exhausted include when the quota resets; monthly_spend_cap_reached means the workspace hit the ceiling its owner set, and only they can raise it; recipient_suppressed lists addresses that previously bounced or complained.

Cold sends carry a signed one-click List-Unsubscribe link. Using it suppresses that address for your workspace immediately, and later sends to it are rejected before they reach SES.

Sending it later

A send can name send_at, up to 30 days out, and comes back as scheduled with a scheduled_id rather than as sent. Use it for the times a message is right but the moment is not: a follow-up on Monday, a reminder the day before, a chase in a week if nobody has replied.

Nothing is decided at booking time. The quotas, the suppression list, the outbound policy and the agent's approval setting are all applied at the moment it actually sends, which is the honest way round: an address that bounces on Tuesday should not receive mail booked on Monday. Ask list_scheduled or GET /v1/messages/scheduled for what is still coming, and GET /v1/messages/scheduled/{id} for what became of one. A booking the agent's approval setting caught gets its own held status carrying a pending_id, because calling it sent or failed would say something untrue about mail nobody has released.

cancel_scheduled, or DELETE on the same URL, calls one off. It answers 409 once the runner has taken it, which is a real answer rather than an error: the message is on its way, so look at what it became rather than retrying the cancel.

When a person reads it first

Agents send their own mail, including first contact, and every agent starts that way. An agent can also be set to hold, which is a per-agent setting on its Settings tab: hold first contact and let replies through, or hold everything. It is the setting for a new agent nobody has watched yet, or a first run of outreach, and it is meant to be turned off again.

A held send comes back as held with a pending_id rather than as an error. That is a third outcome, not a failure: over REST it is a 202, and the message is queued rather than refused. Do not retry it. Retrying a held send is how one message becomes five in the reviewer's queue, all of which they then have to read.

Ask what was decided with check_approval over MCP, or GET /v1/messages/pending/{pending_id} over REST, or subscribe to the approval.approved, approval.rejected and approval.expired webhook events and be told instead of polling. A rejection carries decision_note, the reviewer's own words: it is the only channel through which a person tells an agent what was wrong with what it wrote, so read it before composing a replacement rather than sending the same thing again.

Approving replays the request through the ordinary send path, so the suppression list, the quotas and the spend cap are all checked at the moment it actually goes. Nothing is pre-authorised by having been approved. A message nobody acts on expires after 7 days and will not be sent, because a reply to a question asked a fortnight ago is the wrong message rather than a late one.

Rate limits

Every API key gets 300 requests per minute across the REST API and the MCP server, counted per key. Calls without a key are limited to 30 per minute per IP address, and OAuth client registration to 5 per minute per IP: it is open to anyone by necessity, so it is budgeted tightly and unused registrations are deleted after a day.

Responses carry x-ratelimit-limit and, where available, x-ratelimit-remaining and x-ratelimit-reset. A refusal is a 429 with retry-after in seconds and a body of {"error": "rate_limited"}.