Send returns 422 recipient_suppressed
Cause: One of to/cc/bcc is on your workspace suppression list (hard bounce, complaint, unsubscribe, or manual entry). The check runs before quota is claimed, so nothing was billed.
Fix:
- Find the row:
GET /v1/suppressionsand match the address;details.sourceshows who added it. - If the address is legitimately reachable again (e.g. mailbox restored), remove it:
DELETE /v1/suppressions/{id}. - Retry the send. Never re-add
complaintaddresses without explicit renewed consent.
Send returns 402 — limit_reached vs spend_cap_reached
Cause: Both mean a ceiling stopped the send, but different ones. limit_reached is the monthly/daily allowance or an explicit workspace cap; spend_cap_reached is the spend-protection pause threshold.
Fix:
limit_reached→ enable overage, raise the workspace cap, or upgrade the plan.spend_cap_reached→ go to Billing → Spend protection and raise or remove the cap.- Daily caps reset at midnight UTC; monthly allowance rolls over at the cycle start.
Send returns 403 sending_not_approved
Cause: Workspace sending is paused — pending manual review, or a reputation tripwire fired (spam-complaint or hard-bounce thresholds). The response may include reason, action, and trippedAt.
Fix: Read reason first, clean the cause (stale lists, missing unsubscribe handling), then request sending approval in the console or contact support. Conversational replies stay unaffected only for the bulk tripwire (sending_paused_bulk_review) — a full pause blocks everything.
Message failed — where to look
Cause: Provider rejection, bad recipient, or downstream outage after the send was accepted. Fix:- Read the message row (
GET /v1/messages/{id}):statuspluserrorMessagecarry the provider’s reason. A bounce also emits anemail.bouncedwebhook withdiagnostic. - Check
GET /v1/webhooks/deliveriesfor the matching event to see whether your endpoint acknowledged it. - Transient provider failures surface as
email_delivery_failed(502) with quota refunded — safe to retry. Permanent recipient failures suppress the address instead.
Domain stuck in pending
Cause: DNS records have not propagated, or a record value is wrong. Verification requires the ownership token and the SES MX record; SPF/DKIM/DMARC do not gate it but hurt deliverability when missing.
Fix:
- Re-run
POST /v1/domains/{id}/verifyand readverificationReport.records— each entry showsexpectedvsfoundwith amatchedflag, pinpointing the broken record. - Common culprits: TXT value missing the
gork-verification=prefix, MX pointing at the wrong region host (must beinbound-smtp.ap-southeast-2.amazonaws.com, priority10), or resolvers still serving cached negatives — wait out the TTL and retry. POST /v1/inboxeswith the domain keeps returningdomain_not_verified(400) until status flips toverified.
Webhook not arriving
Cause: Usually one of: endpoint not subscribed to the event, a terminal4xx rejection, or a slow handler exceeding the 10-second timeout.
Fix:
- Confirm subscription:
GET /v1/webhooks→subscribedEventsmust include the event (or"*"). - Inspect
GET /v1/webhooks/deliveries: a4xxresponseStatusmeans your endpoint rejected it — no retry will come, fix the handler. A timeout error means the handler took over 10 seconds — acknowledge fast and defer work. - Only rotate the secret (
POST /v1/webhooks/{id}/rotate-secret) when verification fails, not when deliveries are missing — rotation breaks a working verifier until both sides are updated.
Sandbox cannot send to multiple recipients
Cause: Free-tier sandbox is 1-on-1 only. More than oneto, or any cc/bcc, fails with sandbox_bulk_not_allowed (403).
Fix: Send individual messages, or upgrade to a paid plan for bulk sending.
409 address_in_cooldown when creating an inbox
Cause: The address was recently deactivated and sits in a 90-day security quarantine against account takeover. Same-workspace reactivation restores the inbox instead of erroring — only a different workspace claiming it hits this.
Fix: Pick another username, or wait — the response includes cooldownDaysRemaining. There is no manual override.
Scheduled message stuck
Cause: Ascheduled message only dispatches at its sendAt; if the plan changed, cancel it and start over.
Fix:
- Cancel:
DELETE /v1/messages/{id}/schedule→{ "status": "cancelled" }. The row is retained for audit and can never dispatch afterwards. - If cancel returns
not_scheduled(409), the message already leftscheduledstate — read itsstatusinstead of retrying the cancel. - Resend fresh via
POST /v1/messages/send. Scheduled sends cannot carry attachments (attachments_not_schedulable) — send those immediately instead.