Courier email with Groundmail
Send Courier email notifications through Groundmail by connecting Courier's SMTP integration to the Groundmail SMTP relay. You enter four values in Courier, and your existing templates and routing keep working.
Prerequisites
- A Groundmail account with a verified sending domain.
- An SMTP credential: in the Groundmail dashboard, open SMTP and choose Create SMTP credential. The password (
gmsmtp_…) is shown once, so copy it. See SMTP relay. - A Courier account with an email notification or a call to Courier's Send API.
1. Add the SMTP integration in Courier
Open the SMTP integration in the Courier app (Integrations catalog, then SMTP) and fill in:
| Courier field | Value |
|---|---|
| SMTP host | smtp.groundmail.dev |
| Username | groundmail |
| Password | Your SMTP credential (gmsmtp_…) |
| From Address | An address on your verified domain, for example notifications@yourdomain.com |
Save the integration.
Port and TLS
Groundmail accepts connections on port 587 with STARTTLS. Courier's SMTP documentation recommends the same setup and describes it as the transport settings port: 587 and requireTLS: true. Courier's documentation lists only host, username, password and From Address as dashboard fields, so confirm your integration's port and TLS settings against port 587 with STARTTLS.
If your integration does not let you set the port, Courier documents a per-message override on message.providers.smtp.override.config. Use it in your Send API call:
{
"message": {
"providers": {
"smtp": {
"override": {
"config": { "port": 587, "requireTLS": true }
}
}
}
}
}2. Send a test
Courier's Send API takes the recipient, the content and the channel to route through. Send to a Groundmail simulator address so nothing reaches a real inbox:
curl -X POST https://api.courier.com/send \
-H "Authorization: Bearer $COURIER_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"message": {
"to": { "email": "delivered@simulator.groundmail.dev" },
"content": {
"title": "Groundmail test",
"body": "If you can see this in Groundmail, the SMTP integration works."
},
"routing": { "method": "single", "channels": ["email"] }
}
}'Courier replies with a requestId. Then check Groundmail: open Emails in the dashboard, or run:
curl "https://api.groundmail.dev/emails?limit=5" \
-H "Authorization: Bearer $GROUNDMAIL_API_KEY"The email appears with "test": true and the status delivered. Use bounce@simulator.groundmail.dev to rehearse a bounce. All outcomes are in Testing.
3. Track delivery with webhooks
Courier hands the message to Groundmail over SMTP, and Groundmail reports what happens next. Create a webhook endpoint:
curl -X POST https://api.groundmail.dev/webhooks \
-H "Authorization: Bearer $GROUNDMAIL_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"url": "https://yourapp.com/webhooks/groundmail",
"events": ["email.delivered", "email.delivery_delayed", "email.bounced", "email.complained"]
}'Each event includes the recipient (data.to), the subject and the Groundmail email id (data.email_id). Verify the Groundmail-Signature header before you trust a request; the Knock guide has a complete handler, and Webhooks lists every event.
Troubleshooting
Courier fails the connection check. Courier checks the SMTP connection before each send and does not send the message if the check fails. Re-enter the host, username and password: the username is exactly groundmail, and the password is the SMTP credential (gmsmtp_…), not an API key (gm_…). Then confirm port 587 with STARTTLS as described above.
The message is refused because of the From Address. The domain in From Address must be a verified domain on your account. Until your domain is verified you can only test from the sandbox sender, and only to your own account email.
A recipient never receives the email. The address may be on your suppression list, usually after a hard bounce. List suppressions with GET /suppressions (needs a full-access API key) and see Suppressions.
You hit a sending limit. New accounts ramp up their daily volume, and the Free plan has a daily limit. Mail over the limit is deferred or refused with a message that says which limit applied; see Sending limits.