Email Delivery Log¶
Available to: Platform Admin (Django admin)
Every outbound transactional email — tenant invites, workflow notifications, and platform alerts — is recorded in the Email log (EmailLog model). Use it to confirm whether an email was sent, skipped, or failed, and why.
Where to view the log¶
- Sign in to Django admin at
https://<your-api-host>/django-admin/using a platform admin email and a Django password set viapython manage.py set_django_admin_password <email>on the web service (Auth0 login does not apply here). See Platform Admin. - Open Companies → Email logs.
- Filter by status, category, or template name, or search by recipient address or Mailgun message ID.
The log is read-only. Rows are created automatically by the Celery worker when send_transactional_email runs.
What gets logged¶
| Category | Examples |
|---|---|
invite |
Welcome email when a team member is invited to a tenant |
workflow_notification |
Bid submitted, bid became job, invoice created, payment recorded, etc. |
platform_alert |
New access-request alert to platform admins |
other |
Access-request confirmation, access-granted, and other one-off sends |
Each row stores: recipients, subject, template name, status, Mailgun message ID (when sent), HTTP status, error detail, and timestamp.
Status values¶
| Status | Meaning |
|---|---|
sent |
Mailgun accepted the message. Check provider_message_id in the Mailgun dashboard if the recipient did not receive it (spam, sandbox restrictions, etc.). |
failed |
Mailgun returned an error or the HTTP request failed. See http_status and error_detail. |
skipped_no_config |
MAILGUN_API_KEY or MAILGUN_DOMAIN is not set on the Celery worker. The send was never attempted. |
skipped_cap |
Daily send cap (~100) was reached. |
skipped_no_recipients |
No valid recipient addresses were provided. |
render_failed |
Email template could not be rendered (configuration or template bug). |
Diagnosing missing emails¶
Work through these checks in order:
1. Is there an EmailLog row for the event?¶
- No row at all — the Celery task was never executed. Confirm the
workercomponent is running in App Platform and connected to Redis. Workflow notifications and invites use.delay(); they do not send inline from the web process. - Row with
skipped_no_config— setMAILGUN_API_KEY,MAILGUN_DOMAIN, andDEFAULT_FROM_EMAILon the worker (and web for consistency). Without these, sends are skipped silently except for this log entry. - Row with
skipped_cap— wait until the next UTC day or raise the cap only after upgrading Mailgun. - Row with
failed— inspecthttp_status:401/403usually means a bad API key;4xxon a sandbox domain often means the recipient is not authorized in Mailgun.
2. Tenant invite-specific checks¶
Invite welcome emails are only queued after Auth0 provisioning succeeds. If Auth0 invite fails, see the tenant Activity log (AUTH0_INVITE_FAILED) on the user record — no welcome email is sent.
Required env vars on web (invite is triggered from the API):
AUTH0_MGMT_CLIENT_IDAUTH0_MGMT_CLIENT_SECRETAUTH0_INVITE_ENABLED=True(default)
3. Workflow notification-specific checks¶
Notifications respect Settings → Email notifications per tenant. If the event is disabled or no roles are selected, _dispatch_tenant_event returns without calling Mailgun — no EmailLog row is created for that case.
Trigger a test event (e.g. submit a bid for approval) and then refresh the Email log.
4. Mailgun sandbox vs production domain¶
Sandbox domains only deliver to authorized recipients in the Mailgun dashboard. For production, verify a custom sending domain (e.g. mg.hardhatflow.com) and set MAILGUN_DOMAIN and DEFAULT_FROM_EMAIL to match.
5. Sent but not received¶
If status is sent and provider_message_id is populated, delivery left Hardhat Flow successfully. Use the Mailgun Logs or Suppressions UI with that message ID to trace bounces, spam filtering, or blocks.
Required production environment variables¶
Set these on web and worker in App Platform (worker is required for all .delay() sends):
| Variable | Purpose |
|---|---|
MAILGUN_API_KEY |
Mailgun private API key |
MAILGUN_DOMAIN |
Verified sending domain |
DEFAULT_FROM_EMAIL |
From address matching the Mailgun domain |
FRONTEND_URL |
Links in email bodies (e.g. bid/invoice detail URLs) |
For tenant invites, also on web:
| Variable | Purpose |
|---|---|
AUTH0_MGMT_CLIENT_ID |
Auth0 Management API (M2M) |
AUTH0_MGMT_CLIENT_SECRET |
Auth0 Management API secret |
AUTH0_SPA_CLIENT_ID |
Password-change ticket for invite links |
See Environment Variables for the full catalog, Mailgun operator checklist, and per-component requirements. See also Deployment.