Suomi.fi messages for City of Helsinki Django apps
Project description
django-helsinki-suomifi-messages
Suomi.fi Messages client for City of Helsinki Django apps. See the API reference for endpoint details.
- Send electronic messages to recipients with active Suomi.fi mailboxes
- Send multichannel messages (electronic or paper mail, depending on mailbox status)
- Send paper mail without a personal identity code or business ID
- Check whether recipients have active Suomi.fi mailboxes
- Retrieve message events and read received messages
- Upload and download attachments
Contents
Installation
Requires Python 3.10+ and Django 5.2+.
pip install django-helsinki-suomifi-messages
No changes to INSTALLED_APPS are needed; this is a pure client library.
Configuration
Add the following to your Django settings:
# Suomi.fi API credentials
SUOMIFI_USERNAME = "your-api-username"
SUOMIFI_PASSWORD = "your-api-password"
SUOMIFI_SERVICE_ID = "your-service-id"
# Posti Messaging Oy credentials (required for paper mail only)
# Obtained during paper mail deployment:
# https://kehittajille.suomi.fi/services/messages/deployment/deployment-of-the-printing-enveloping-and-distribution-service
SUOMIFI_POSTI_EMAIL = "your-posti-email"
SUOMIFI_POSTI_USERNAME = "your-posti-username"
SUOMIFI_POSTI_PASSWORD = "your-posti-password"
All settings default to an empty string if not set. SUOMIFI_USERNAME,
SUOMIFI_PASSWORD, and SUOMIFI_SERVICE_ID can also be passed directly to
the relevant client methods. Posti credentials (SUOMIFI_POSTI_*) must always
be configured in Django settings.
Quick start
from suomifi_messages import SuomiFiClient
from suomifi_messages.schemas import Address, BodyFormat
client = SuomiFiClient() # QA environment; use type="prod" for production
client.login() # uses SUOMIFI_USERNAME / SUOMIFI_PASSWORD from settings
recipient_address = Address(
name="Matti Meikäläinen",
street_address="Esimerkkikatu 1",
zip_code="00100",
city="Helsinki",
country_code="FI"
)
sender_address = Address(
name="Helsingin kaupunki",
street_address="Lähettäjänkatu 1",
zip_code="00100",
city="Helsinki",
country_code="FI"
)
with open("letter.pdf", "rb") as f:
attachment_id = client.upload_attachment("letter.pdf", f)
message_id, external_id = client.send_multichannel_message(
title="This is a title",
body="Hello, world!",
body_format=BodyFormat.TEXT,
recipient_id="123456-789A",
recipient_address=recipient_address,
sender_address=sender_address,
paper_mail_attachment_id=attachment_id,
)
Usage
Creating a client
from suomifi_messages import SuomiFiClient
# QA environment (default)
client = SuomiFiClient()
# More explicit
client = SuomiFiClient(type="qa")
# Production environment
client = SuomiFiClient(type="prod")
Authentication
# Uses SUOMIFI_USERNAME and SUOMIFI_PASSWORD from Django settings
client.login()
# Or pass credentials explicitly
client.login(username="user", password="pass")
# Change password; re-login required afterwards (token is invalidated)
client.change_password(current_password="old-pass", new_password="new-pass")
Checking mailbox status
# Check multiple recipients at once
active_ids = client.check_mailboxes(["123456-789A", "987654-321B"])
# Check a single recipient
has_mailbox = client.check_mailbox("123456-789A") # True / False
Sending a multichannel message
A multichannel message is delivered electronically to recipients with an active mailbox, or as paper mail to those without:
from suomifi_messages.schemas import Address, BodyFormat
recipient_address = Address(
name="Matti Meikäläinen",
street_address="Esimerkkikatu 1",
zip_code="00100",
city="Helsinki",
country_code="FI"
)
sender_address = Address(
name="Helsingin kaupunki",
street_address="Lähettäjänkatu 1",
zip_code="00100",
city="Helsinki",
country_code="FI"
)
# Upload the paper mail attachment first
with open("letter.pdf", "rb") as f:
attachment_id = client.upload_attachment("letter.pdf", f)
message_id, external_id = client.send_multichannel_message(
title="Title",
body="Hello, world!",
body_format=BodyFormat.TEXT, # or BodyFormat.MARKDOWN
recipient_id="123456-789A",
recipient_address=recipient_address,
sender_address=sender_address,
paper_mail_attachment_id=attachment_id,
)
All send methods return a (message_id, external_id) tuple:
message_id: the Suomi.fi identifier assigned to the message. Use this to reply to the message or to look it up viaget_message().external_id: your own identifier for the message, used for idempotency. If not provided, a UUID is generated automatically. Sending a message with the sameexternal_idtwice will raiseSuomiFiDuplicateMessageError.
Sending an electronic message
Use this to send a new message or reply to a message from an end user:
message_id, external_id = client.send_electronic_message(
title="Title",
body="Hello, world!",
body_format=BodyFormat.TEXT,
recipient_id="123456-789A",
reply_to=original_message_id,
reply_allowed=True,
)
Sending paper mail without an identity code
Use this when the recipient's identity code is not known or not required. The
recipient is identified solely by their postal address. Using the same Address
setup as above:
with open("letter.pdf", "rb") as f:
attachment_id = client.upload_attachment("letter.pdf", f)
message_id, external_id = client.send_paper_mail_without_id(
recipient_address=recipient_address,
sender_address=sender_address,
attachment_id=attachment_id,
)
Reading events and messages
# Fetch events; pass continuation_token back in subsequent calls for pagination
events, continuation_token = client.get_events()
for event in events:
message = client.get_message(event.metadata.message_id)
# process message...
# Download an attachment from a received message
content = client.get_attachment(attachment_id) # bytes
Error handling
from suomifi_messages import (
SuomiFiAPIError,
SuomiFiClientError,
SuomiFiDuplicateMessageError,
SuomiFiServerError,
)
try:
message_id, external_id = client.send_electronic_message(...)
except SuomiFiDuplicateMessageError as e:
# 409: message with this external_id already sent
# e.message_id contains the original message ID
existing_id = e.message_id
except SuomiFiClientError as e:
# 4xx: bad request, mailbox not active, etc.
print(e.response_body)
except SuomiFiServerError as e:
# 5xx: retry later
raise
except SuomiFiAPIError as e:
# Unexpected non-2xx
raise
The exception hierarchy is:
SuomiFiError
└── SuomiFiAPIError
├── SuomiFiClientError
│ └── SuomiFiDuplicateMessageError
└── SuomiFiServerError
For developers
Prerequisites
Testing
Run the tests with:
hatch test
Test all environments in the matrix with:
hatch test -a
Available Hatch scripts
| Command | Description | Example |
|---|---|---|
hatch run test <args> |
Run pytest directly | hatch run test -k login |
hatch run lint |
Install and run pre-commit hooks | hatch run lint |
hatch run manage <args> |
Run Django management commands | hatch run manage migrate |
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_helsinki_suomifi_messages-1.0.0.tar.gz.
File metadata
- Download URL: django_helsinki_suomifi_messages-1.0.0.tar.gz
- Upload date:
- Size: 26.9 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.7
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
1e4d86f2e0da31db5d45535f8fd21265e09c3656e5f3e4a05ea155e88c15e811
|
|
| MD5 |
122852e75106a5e6144c6d2fa3e51f9d
|
|
| BLAKE2b-256 |
123e390cb18035409675caf5e123a688e433975005d2526d8327257eb550b633
|
Provenance
The following attestation bundles were made for django_helsinki_suomifi_messages-1.0.0.tar.gz:
Publisher:
publish.yml on City-of-Helsinki/django-helsinki-suomifi-messages
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
django_helsinki_suomifi_messages-1.0.0.tar.gz -
Subject digest:
1e4d86f2e0da31db5d45535f8fd21265e09c3656e5f3e4a05ea155e88c15e811 - Sigstore transparency entry: 1134206773
- Sigstore integration time:
-
Permalink:
City-of-Helsinki/django-helsinki-suomifi-messages@5eabe1a3a3ff678cbd92c41df65ac19217f65771 -
Branch / Tag:
refs/heads/main - Owner: https://github.com/City-of-Helsinki
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@5eabe1a3a3ff678cbd92c41df65ac19217f65771 -
Trigger Event:
push
-
Statement type:
File details
Details for the file django_helsinki_suomifi_messages-1.0.0-py3-none-any.whl.
File metadata
- Download URL: django_helsinki_suomifi_messages-1.0.0-py3-none-any.whl
- Upload date:
- Size: 16.7 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.7
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
550c409eec3f06b6e2814d69822fddcec7942fa5806e2da65ec4561b65ff2e69
|
|
| MD5 |
6f369018a563f8ab4ab107cc607cf2f0
|
|
| BLAKE2b-256 |
7c1530b8e584f07aa1c2efe167046ec7dfa941a6b38735de2afd3c8c78a04602
|
Provenance
The following attestation bundles were made for django_helsinki_suomifi_messages-1.0.0-py3-none-any.whl:
Publisher:
publish.yml on City-of-Helsinki/django-helsinki-suomifi-messages
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
django_helsinki_suomifi_messages-1.0.0-py3-none-any.whl -
Subject digest:
550c409eec3f06b6e2814d69822fddcec7942fa5806e2da65ec4561b65ff2e69 - Sigstore transparency entry: 1134207076
- Sigstore integration time:
-
Permalink:
City-of-Helsinki/django-helsinki-suomifi-messages@5eabe1a3a3ff678cbd92c41df65ac19217f65771 -
Branch / Tag:
refs/heads/main - Owner: https://github.com/City-of-Helsinki
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@5eabe1a3a3ff678cbd92c41df65ac19217f65771 -
Trigger Event:
push
-
Statement type: