A reusable Django app to send emails via GOV.UK notify robustly and with good observability
Project description
django-govuk-notify-outbox
A reusable Django app to send emails via GOV.UK notify robustly and with good observability.
Contents
Features
- Sends emails outside of the request/response cycle (via Celery)
- Automatically retries communication with GOV.UK Notify up to a maximum number / maximum amount of time
- Usage is consistent with the
send_email_notificationfunction from notifications-python-client - Emails and updates from GOV.UK Notify (including HTTP errors and Python exceptions) are stored in Django models...
- ... so can be linked (via foreign key or many-to-many relation) to other models to be used in the user-facing application
- ... and are visible in the Django admin interface
Prerequisites
- An existing (even if newly created) Django project
- ... which is configured with Celery and Celery Beat to run background tasks
- A GOV.UK Notify service API Key
Installation
-
Install django-govuk-notify-outbox from PyPI.
Typically you would do this by adding django-govuk-notify-outbox as a dependency to your existing Django project, for example with uv:
uv add django-govuk-notify-outbox
-
Add
django_govuk_notify_outboxtosettings.INSTALLED_APPSINSTALLED_APPS = [ # ... "django_govuk_notify_outbox", ]
-
Add
settings.DJANGO_GOVUK_NOTIFY_OUTBOX__API_KEYwith your GOV.UK Notify API key, typically from an environment variable.import os DJANGO_GOVUK_NOTIFY_OUTBOX__API_KEY = os.environ.get('DJANGO_GOVUK_NOTIFY_OUTBOX__API_KEY')
-
Add
django_govuk_notify_outbox.tasks.synctosettings.CELERY_BEAT_SCHEDULEon a frequent schedule, for example every 5 seconds. If there is nothing to do, this will finish quickly and without connecting to GOV.UK notify.CELERY_BEAT_SCHEDULE = { "sync": { "task": "django_govuk_notify_outbox.tasks.sync", "schedule": 5, }, }
-
Run
python manage.py migrateto create the models. Often projects are setup to do this automatically on startup.
Usage
Fire and forget
Once installed, emails can be sent from any part of your Django project with the django_govuk_notify_outbox.utils.send_email_notification function, which is designed to be consistent with the "fire and forget"-style send_email_notification function from notifications-python-client
from django_govuk_notify_outbox.utils import send_email_notification
notifications_client.send_email_notification(
email_address="amala@example.com",
template_id="9d751e0e-f929-4891-82a1-a3e1c3c18ee3",
)
Attaching to your own models
The return value of send_email_notification is an instance of the Django model django_govuk_notify_outbox.models.EmailMessage, and it can be attached to your own models in the usual way via ForeignKey or ManyToManyField. Each EmailMessage is updated through the email sending process until is is delivered or failed.
For example:
# In your app's models.py
from django.db import models
from django_govuk_notify_outbox.models import EmailMessage
class MyModel(models.Model):
email_messages = models.ManyToManyField(EmailMessage)
# Anywhere in your app
email_message = notifications_client.send_email_notification(
email_address="amala@example.com",
template_id="9d751e0e-f929-4891-82a1-a3e1c3c18ee3",
)
my_model_instance = MyModel.create()
my_model_instance.email_messages.add(email_message)
print(email_message.status) # See below
Email status descriptions
Each EmailMessage.status field can be equal to one of 10 possible values. For the value that reflect a GOV.UK Notify status, see https://docs.notifications.service.gov.uk/python.html#email-status-descriptions for details.
| State | Description |
|---|---|
sending-to-notify |
The email is about to be sent to GOV.UK Notify (or has very recently been sent). |
retrying-send-to-notify |
The email has failed sending to GOV.UK Notify at least once and will be retried (or has very recently been retried). |
failed-send-to-notify |
The email has failed sending to GOV.UK Notify and will not be retried. |
sent-to-notify |
The email has been sent to GOV.UK Notify |
notify-created |
GOV.UK notify reports the email in the created state, which essentially means queued: send will be attempted in the next few seconds. |
notify-sending |
GOV.UK notify reports the email in the sending state. |
notify-delivered |
GOV.UK notify reports the email in the delivered state. |
notify-temporary-failure |
GOV.UK notify reports the email in the temporary-failure state. Note that this does not mean that the email will be retried automatically, but more reflects that if another identical email is sent, it may succeed. |
notify-technical-failure |
GOV.UK Notify reports the email in the technical-failure state, which indicates a technical issue between Notify and its email provider. It will not be retried automatically. |
notify-permanent-failure |
GOV.UK notify reports the email in the permanent-failure state, which indicates it is not likely to succeed if another identical email is sent. |
Happy path
5 statuses make up the happy path when there are no problems sending an email:
stateDiagram-v2
sendingToNotify: sending-to-notify
sentToNotify: sent-to-notify
notifyCreated: notify-created
notifySending: notify-sending
notifyDelivered: notify-delivered
[*] --> sendingToNotify
sendingToNotify --> sentToNotify
sentToNotify --> notifyCreated
notifyCreated --> notifySending
notifySending --> notifyDelivered
notifyDelivered --> [*]
Happy and unhappy paths
How EmailMessage.status can change in all cases of both success and failure is shown in the following diagram:
stateDiagram-v2
sendingToNotify: sending-to-notify
retryingSendToNotify: retrying-send-to-notify
sentToNotify: sent-to-notify
failedSendToNotify: failed-send-to-notify
notifyCreated: notify-created
notifySending: notify-sending
notifyDelivered: notify-delivered
notifyPermanentFailure: notify-permanent-failure
notifyTemporaryFailure: notify-temporary-failure
notifyTechnicalFailure: notify-technical-failure
[*] --> sendingToNotify
sendingToNotify --> retryingSendToNotify
retryingSendToNotify --> failedSendToNotify
sendingToNotify --> sentToNotify
retryingSendToNotify --> sentToNotify
failedSendToNotify --> [*]
sentToNotify --> notifyCreated
notifyCreated --> notifySending
notifySending --> notifyDelivered
notifySending --> notifyPermanentFailure
notifySending --> notifyTemporaryFailure
notifySending --> notifyTechnicalFailure
notifyPermanentFailure --> [*]
notifyTemporaryFailure --> [*]
notifyTechnicalFailure --> [*]
notifyDelivered --> [*]
Configuration
Configuration is via settings that go into your Django project's settings.py
| Setting | Description |
|---|---|
DJANGO_GOVUK_NOTIFY_OUTBOX__API_KEY |
The GOV.UK Notify API key for the service |
DJANGO_GOVUK_NOTIFY_OUTBOX__SEND_TO_NOTIFY_RETRY_INTERVALS |
A list of how long to wait in seconds between retrying send to GOV.UK Notify Default: [5,10,60,120,240,480,960,1920] |
DJANGO_GOVUK_NOTIFY_OUTBOX__SEND_TO_NOTIFY_ABANDON_AFTER |
How long in seconds to wait to abandon send to GOV.UK notify, even if there are remaining retries according to the DJANGO_GOVUK_NOTIFY_OUTBOX__SEND_TO_NOTIFY_RETRY_INTERVALS setting. This is used to not send emails if the background worker has been offline for a long period of time and so the emails are no longer relevant.Default: 3600 |
License
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file django_govuk_notify_outbox-1.5.0.tar.gz.
File metadata
- Download URL: django_govuk_notify_outbox-1.5.0.tar.gz
- Upload date:
- Size: 14.5 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.13
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
cb1e0e1a33602ebb995359d5fe89b81c2e4e7116c3e897910dedae86a198e615
|
|
| MD5 |
3e238ef44ec6f203fc8a353c6f1461e5
|
|
| BLAKE2b-256 |
a61ee3c1a1d0668302a7d08e8c5d9951a8df9ed9e914bdf511dd03b51ef67ed3
|
Provenance
The following attestation bundles were made for django_govuk_notify_outbox-1.5.0.tar.gz:
Publisher:
release.yml on uktrade/django-govuk-notify-outbox
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
django_govuk_notify_outbox-1.5.0.tar.gz -
Subject digest:
cb1e0e1a33602ebb995359d5fe89b81c2e4e7116c3e897910dedae86a198e615 - Sigstore transparency entry: 1281030431
- Sigstore integration time:
-
Permalink:
uktrade/django-govuk-notify-outbox@25b7bc91c986d1c1641027e16b9b2531ed65ed41 -
Branch / Tag:
refs/heads/main - Owner: https://github.com/uktrade
-
Access:
internal
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yml@25b7bc91c986d1c1641027e16b9b2531ed65ed41 -
Trigger Event:
push
-
Statement type:
File details
Details for the file django_govuk_notify_outbox-1.5.0-py3-none-any.whl.
File metadata
- Download URL: django_govuk_notify_outbox-1.5.0-py3-none-any.whl
- Upload date:
- Size: 18.3 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.13
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
01eca875b4611b2d5731ede3a89abb7a3d31ddaf7fbab838dcb8ebdbc7cc9bfb
|
|
| MD5 |
afa33ebf92862e6befe466dd43776464
|
|
| BLAKE2b-256 |
4fedf9d2a9a822473288ccbab1f8be1339c6a4eea2e1c89b6bacf9c351dcb246
|
Provenance
The following attestation bundles were made for django_govuk_notify_outbox-1.5.0-py3-none-any.whl:
Publisher:
release.yml on uktrade/django-govuk-notify-outbox
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
django_govuk_notify_outbox-1.5.0-py3-none-any.whl -
Subject digest:
01eca875b4611b2d5731ede3a89abb7a3d31ddaf7fbab838dcb8ebdbc7cc9bfb - Sigstore transparency entry: 1281030439
- Sigstore integration time:
-
Permalink:
uktrade/django-govuk-notify-outbox@25b7bc91c986d1c1641027e16b9b2531ed65ed41 -
Branch / Tag:
refs/heads/main - Owner: https://github.com/uktrade
-
Access:
internal
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yml@25b7bc91c986d1c1641027e16b9b2531ed65ed41 -
Trigger Event:
push
-
Statement type: