Pick what you're seeing. Each fix says how to check it and what to do next.
- Emails aren't arriving: sent, but not in the inbox, or stuck before sending
- Domain won't verify: DNS records added but the domain isn't verified
- Going to spam: delivered, but landing in junk
- API errors: 401, 403, 422 and 429, and what they mean
- Webhooks not arriving: events you expect don't reach your endpoint
Emails aren't arriving
Start with the email itself. Open it on the Emails page in the dashboard, or look it up by the msg_… id the send returned:
groundmail emails get --id msg_abc123Its status says where it stopped:
| Status | What it means | What to do |
|---|---|---|
scheduled |
It goes out at scheduled_at |
Nothing, or reschedule or cancel it |
held |
Held for content review, or sending is paused on the account | Held email is reviewed quickly and then sent or rejected; you get a webhook either way |
suppressed |
A recipient is on your suppression list, or Groundmail validation held every recipient back | Check details.suppressed or held_back. Remove the suppression only if you know the address works now |
failed |
It was rejected, for example by content scanning | Read bounce_reason |
sent |
Accepted for delivery and on its way | Wait for delivered or bounced. A slow receiving server can take a few minutes |
bounced |
The receiving server refused it | bounce_type says why: hard means the address can't receive (it's suppressed now); block means the server refused the sender, not the address |
delivered |
The recipient's mail provider accepted it | It's in their mailbox system: check spam and filters on their side (Going to spam) |
The send itself returned an error
Nothing was sent. The error body's code and hint say what to fix: see API errors.
Only your own address gets mail
Sandbox sends (from noreply@mail.groundmail.dev) only go to your account email. To send to anyone else you need a verified domain and a payment method on file at Settings → Billing. The Free plan is never charged.
It's a test send
Sends with a test key (gm_test_…) and sends to @simulator.groundmail.dev addresses run every check but deliver nothing. They're marked test: true. See Testing.
Domain won't verify
Run verify again from the domain's page in the dashboard, or:
groundmail domains verify yourdomain.comThe response says which records are missing. Then check these, most common first.
DNS hasn't propagated yet
New records can take a few minutes to show up in public DNS, sometimes longer at some hosts. Wait, then verify again. Re-running verify is always safe.
Two SPF records
A domain can only have one SPF record. If you already send from another service, add include:spf.groundmail.dev to your existing SPF record instead of adding a second one:
v=spf1 include:_spf.google.com include:spf.groundmail.dev ~allThe record is on the wrong name
DNS hosts handle names differently. Some want the full name (_groundmail.yourdomain.com), others only the part before your domain (_groundmail); entering the full name in the second kind gives _groundmail.yourdomain.com.yourdomain.com. Compare what you added with the names in the dashboard, and make sure the records are in the zone that actually serves your domain.
The DKIM value was split or cut short
The DKIM TXT value is long. Paste it exactly as shown, as one value; some hosts split long values into quoted chunks, which is fine, but a value cut short fails. If your DNS is on Cloudflare, Vercel or GoDaddy, let Groundmail add the records for you instead: see Apply DNS.
Public DNS looks right, but verify still fails
Verify also waits for the mail server to accept the DKIM and return-path records, which can lag behind public DNS. The response shows mail_server_signing.dkim_ok and return_path_ok; wait a few minutes and verify again. Details: Verify DNS.
Going to spam
- Send from your own verified domain, not the sandbox sender. Verification turns on DKIM signing with your domain, so DMARC can pass.
- Add a DMARC record if your domain has none. Start with
p=noneand watch the reports with DMARC monitoring. - Keep transactional and marketing apart. Password resets and receipts go out as
transactional; onboarding tips and updates asproduct, which sends from separate IPs. See Transactional and product streams. - Check your bounce and complaint rates on the Deliverability page or with
GET /deliverability. Bounces above 2% or complaints above 0.1% hurt inbox placement. - Look at the content. Link shorteners, image-only emails and a mismatched
fromname all count against you. An email that scores badly in content scanning is held or rejected before it goes out.
API errors
Every error has a stable code, a message, a hint that says how to fix it, and a docs_url. Branch on code, never on message.
| Status | Usual cause | Fix |
|---|---|---|
401 |
Missing or wrong API key | Send Authorization: Bearer gm_… with a key that hasn't been revoked |
403 |
from isn't on a verified domain, a sandbox send to someone other than you, or a key without the scope |
Read hint. See forbidden and insufficient_scope |
400 |
A missing or invalid field, or invalid JSON | Read message and details. Every send needs from, to, subject and html or text |
422 |
A recipient is suppressed or opted out | See details.suppressed, details.category_unsubscribed or details.held_back |
429 |
Rate limit, plan limit or the new-account ramp-up | rate_limited: back off and retry. plan_limit: upgrade. daily_ramp_limit: send the rest after details.resets_at. See Sending limits |
5xx |
Something failed on our side | Retry with backoff and the same Idempotency-Key, so a retry never sends twice |
The full list, with what to do for each code: Errors.
Webhooks not arriving
- Check the endpoint is subscribed to the event. Each endpoint lists the events it gets;
email.deliveredandemail.bouncedare separate. - Check the signature with the raw body. Verify
Groundmail-Signatureagainst the request body exactly as received, before any JSON parsing. See Signature verification. - Answer quickly with a 2xx. Do the work after you respond. A delivery that doesn't get a
2xxis retried 4 more times over about 5 minutes. - Send a test event with
POST /emails/testto see what your endpoint receives, or send to a simulator address to get every event of a real send without delivering mail.
Still stuck?
Use the chat button in the dashboard, or email support@groundmail.dev. Include the msg_… id or the domain name; we can see the rest, so there's no need to send keys or logs.