Operations

Email delivery

Runbook for SendGrid and cPanel delivery, including DNS and troubleshooting steps.
Runbook

What this covers

Provider setup, DNS records, authentication, and troubleshooting steps for delivery and reputation issues.

Email delivery reference

# Email Delivery (Brevo/SendGrid + cPanel)

This app sends email through the notification outbox. The API drains the outbox and delivers via Brevo or SendGrid Web API.

## Environment

Set these in `.env` (API server):

```
EMAIL_PROVIDER=brevo
BREVO_API_KEY=your-brevo-api-key
# Alternative:
# EMAIL_PROVIDER=sendgrid
# SENDGRID_API_KEY=your-sendgrid-api-key
EMAIL_FROM="BookYoga <info@bookyoga.in>"
WEB_BASE_URL=https://bookyoga.in
```

## Brevo setup

1. Create a transactional email API key.
2. Verify the sender or authenticate your domain.
3. Keep API-key IP restrictions disabled unless Cloud Run has a stable outbound IP.
4. Set `EMAIL_PROVIDER=brevo`.

## SendGrid setup

1. Create an API key with Mail Send permissions.
2. Verify the sender or authenticate your domain.
3. Test with password reset:

```
curl -X POST http://localhost:3001/api/auth/forgot-password \
  -H "Content-Type: application/json" \
  -d '{"email":"info@bookyoga.in"}'
```

The outbox will drain every minute. You can also drain manually via the admin UI.

## cPanel mailbox (info@bookyoga.in)

Make sure the mailbox exists and email routing is set to Local Mail Exchanger.

DNS records (GoDaddy):

```
Type: A
Host: mail
Points to: <cpanel-server-ip>

Type: MX
Host: @
Points to: mail.bookyoga.in
Priority: 10
```

Verify with:

```
dig mx bookyoga.in
dig a mail.bookyoga.in
```

## Admin tools

- Admin UI: `/dashboard/admin/notifications`
- Drain pending: sends queued items with advisory lock
- Force drain: bypasses the lock if a previous drain is stuck

## Troubleshooting

- `Deferred` with timeout: MX missing or mail server unreachable.
- `550 relay not permitted`: mailbox not local on cPanel or mail routing set to Remote.
- `Dropped` or `Bounced`: check SendGrid Suppressions and remove the address, then retry.