Groundmail — Quickstart

Send your first transactional email in about five minutes.

Migrating from another provider? Start with Resend, Postmark, Amazon SES, or SendGrid, then mirror send + webhook flows from the hosted API Explorer.

Building auth email? See password reset, Supabase Auth, and Clerk / Auth.js.

Using React Email components? See Send React Email.

Hosted docs: docs.groundmail.dev
Dashboard: app.groundmail.dev
API: https://api.groundmail.dev


For AI agents

Use the dashboard Get started page to copy a personalized setup prompt with your API key and sandbox rules.


1. Create an account

  1. Open the dashboard at app.groundmail.dev.
  2. Sign up with email + password (or email code if you prefer).
  3. If using email code: enter the code emailed to you.

2. Send a sandbox test (fastest path)

On Get started (/overview), choose one of two doors:

Set it up here (dashboard)

  1. Click Send test email. We create an API key if needed and send to your account email (sandbox sender before DNS; your verified domain after).
  2. Check your inbox for the test message.
  3. Copy the gm_… secret if shown (once).

No domain or DNS required for the first sandbox send. After you verify a domain, the same button self-tests from that domain (still to your account email on Sandbox).

Use with Cursor or Claude

  1. On Get started, click Copy setup prompt or Install in Cursor.
  2. Paste the prompt into your editor, or add the groundmail-mcp MCP server with your API key.
  3. Let the agent wire POST /emails (or the SDK) into your app and run the sandbox self-test.

See docs/ai/agent-skill.md and docs/ai/mcp.md.


3. Create an API key (if you skipped the dashboard flow)

  1. Go to API Keys → Create (or use an existing gm_… key).
  2. Copy the secret — it is shown once.
export GROUNDMAIL_API_KEY=gm_your_key_here

4. Verify a sending domain (DNS setup — self-test from your domain on free)

Add and verify your domain on Sandbox to complete DNS setup. On the free plan you can self-test from your verified domain to your account email to confirm DNS and branding. Free production (send to other recipients) unlocks after a verified domain and a payment method on file — we do not charge until you upgrade. Caps: 3,000 emails/mo · 100/day · 1 domain. Upgrade to Pro or Scale for more volume.

  1. Optional but easiest: Settings → Integrations → paste a Cloudflare API token (Zone DNS Edit) → Save.
  2. Go to Domains → Add domain and enter your domain (e.g. yourdomain.com).
  3. On the domain page:
    • If Cloudflare is connected: click Apply DNS (Cloudflare), then Verify DNS.
    • If your DNS is at GoDaddy: switch to GoDaddy, paste an API key + secret for a one-time apply, then Verify DNS.
    • Otherwise paste the shown TXT records at your DNS host (merge SPF if you already have one), then Verify DNS.
  4. Optional after verification: set Delivery settings on the domain detail page for:
    • open tracking
    • click tracking
    • tls_mode (opportunistic or enforced)
  5. Check the built-in DMARC/BIMI guidance on the same page before you roll out branded inbox features.
Type Purpose
TXT _groundmail.yourdomain.com Domain ownership
TXT {selector}._domainkey.yourdomain.com DKIM (domain signing key)
CNAME rp.yourdomain.com Return path → rp.groundmail.dev
TXT yourdomain.com SPF (include:spf.groundmail.dev)
TXT _dmarc.yourdomain.com DMARC

Ownership, SPF, DKIM, and return-path are required to verify. Verify also confirms mail-server DKIM readiness so sends are signed with your domain (not the shared pool). Local Docker auto-verifies when GROUNDMAIL_DEV_AUTO_VERIFY=true. If a domain is already attached to another Groundmail team, POST /domains returns a 409 with claim instructions instead of creating a duplicate owner.

CLI (from your editor / terminal)

Save a Cloudflare token under Settings → Integrations first (recommended). Then:

export GROUNDMAIL_API_KEY=gm_your_key_here

npx -y --package=groundmail-cli -- groundmail domains add yourdomain.com

# Uses the Cloudflare token stored in Groundmail (no local CLOUDFLARE_API_TOKEN needed)
npx -y --package=groundmail-cli -- groundmail domains apply yourdomain.com --provider cloudflare

npx -y --package=groundmail-cli -- groundmail domains verify yourdomain.com

Optional override: set CLOUDFLARE_API_TOKEN locally to apply with a machine token instead of the dashboard one. Vercel uses VERCEL_API_TOKEN and optional VERCEL_TEAM_ID the same way. GoDaddy can use stored dashboard credentials or local GODADDY_API_KEY / GODADDY_API_SECRET.


5. Send from your app

Pick one integration method below. Replace you@yourdomain.com with an address on your verified domain.

Sandbox self-test (before DNS)

Use the shared sandbox sender on mail.groundmail.dev, but only send to your own account email:

curl -X POST https://api.groundmail.dev/emails \
  -H "Authorization: Bearer $GROUNDMAIL_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "from": "noreply@mail.groundmail.dev",
    "to": "you@example.com",
    "subject": "Sandbox check",
    "html": "<p>Groundmail sandbox works.</p>"
  }'

Once your domain is verified, you can self-test from your own domain to your account email:

curl -X POST https://api.groundmail.dev/emails \
  -H "Authorization: Bearer $GROUNDMAIL_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "from": "you@yourdomain.com",
    "to": "you@example.com",
    "subject": "Verified domain check",
    "html": "<p>My domain works.</p>"
  }'

Add a payment method at Settings → Billing to unlock Free production (send to other recipients). Upgrade to Pro or Scale for more volume.

curl (production)

curl -X POST https://api.groundmail.dev/emails \
  -H "Authorization: Bearer $GROUNDMAIL_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "from": "you@yourdomain.com",
    "to": "user@example.com",
    "subject": "Hello from Groundmail",
    "html": "<p>It works.</p>"
  }'

Node.js SDK

npm install groundmail
import { Groundmail } from 'groundmail'

const client = new Groundmail(process.env.GROUNDMAIL_API_KEY!)

const { id, status } = await client.emails.send({
  from: 'you@yourdomain.com',
  to: 'user@example.com',
  subject: 'Hello from Groundmail',
  html: '<p>It works.</p>',
})

console.log(id, status)

CLI

GROUNDMAIL_API_KEY=$GROUNDMAIL_API_KEY npx -y --package=groundmail-cli -- groundmail emails send \
  --from you@yourdomain.com \
  --to user@example.com \
  --subject "Hello from Groundmail" \
  --html "<p>It works.</p>"

Or install globally: npm install -g groundmail-cli, then run groundmail emails send ….


Success response

{
  "id": "msg_abc123…",
  "status": "sent"
}

Plan limits

When billing is enabled, Sandbox is for integration and self-test sends (hard daily and monthly caps). Until Free production is unlocked, sends are limited to your account email — from the shared sandbox sender or from a verified domain. Unlock Free production with a verified domain and a payment method on file (no charge until you upgrade). Paid Pool and Dedicated plans meter overage past included volume. Over Free/Sandbox limit:

{ "error": { "message": "…", "code": "plan_limit", "upgrade_url": "/settings/billing" } }

HTTP 429. Upgrade in the dashboard under Settings → Billing.

Common errors

HTTP Meaning Fix
401 Invalid or missing API key Check Authorization: Bearer gm_…
403 From domain not verified, sandbox/verified-domain recipient mismatch, or send to other recipients without Free production unlocked Verify domain; self-test to your account email; add a payment method at Settings → Billing to unlock Free production
400 Missing from, to, or subject Send required fields + html or text
429 Plan send limit (code: plan_limit) Upgrade at Settings → Billing
503 Mail delivery temporarily unavailable Retry later or contact support

Full reference: docs.groundmail.dev/api-reference


6. Webhooks (optional)

  1. Go to Webhooks in the dashboard (or use the API).
  2. Add your HTTPS endpoint URL.
  3. Copy the signing secret (whsec_…) — shown once.
  4. Verify Groundmail-Signature on incoming POSTs.
await client.webhooks.create({
  url: 'https://yourapp.com/webhooks/groundmail',
})

See docs.groundmail.dev/api-reference for payload shape and signature verification.


7. Receive email (optional)

Want an app inbox or agent mailbox?

  1. Create a dedicated inbound subdomain such as inbound.example.com.
  2. Add it with inbound_enabled: true (API) or groundmail domains add inbound.example.com --inbound true (CLI).
  3. Verify the domain, then publish the returned MX record.
  4. Subscribe to email.received and/or poll GET /received-emails.

See docs/api/inbound.md for the full flow and the MX conflict warning.


8. Event-driven automations (optional)

Groundmail can trigger transactional sequences from custom events.

Dashboard: open Automations, create from a starter (or blank) on the visual canvas, configure steps in the side panel (send-email can use a published Templates entry or inline HTML), then Activate.

CLI / API: create and activate from a JSON file (handy in Cursor or CI):

groundmail automations create --file ./welcome.json
groundmail automations activate --id "$AUTOMATION_ID"

Then send the trigger event from your app:

curl -X POST https://api.groundmail.dev/events \
  -H "Authorization: Bearer $GROUNDMAIL_API_KEY" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: evt_user_created_123" \
  -d '{
    "name": "user.created",
    "user_id": "user_123",
    "email": "user@example.com",
    "data": {
      "name": "Ada Lovelace"
    }
  }'

Response:

{
  "event": {
    "id": "ae_123",
    "name": "user.created",
    "user_id": "user_123",
    "email": "user@example.com",
    "data": { "name": "Ada Lovelace" },
    "source": "api",
    "created_at": "2026-07-26T12:00:00.000Z"
  },
  "matched_automations": 1,
  "resumed_runs": 0,
  "cancelled_runs": 0
}

See docs/api/automations.md and docs/api/events.md.


Next steps