Skip to main content

norialabs-notify

Python client for Noria Notify: transactional email and SMS through one internal service. Sync and async, typed, no dependency beyond httpx.

pip install norialabs-notify      # or: uv add norialabs-notify
from noria_notify import Notify

notify = Notify(api_key=os.environ["NORIA_NOTIFY_KEY"], base_url="https://notify.noria.co.ke")

notify.emails.send({
    "from_": "Noria <hello@norialabs.com>",
    "to": "founder@example.com",
    "subject": "Welcome",
    "html": "<p>Hi there</p>",
})

notify.sms.send({"from_": "NORIA", "to": "0712345678", "text": "Your code is 482913"})

from_ carries the trailing underscore because from is a keyword; it is sent as from. Every other field is exactly what goes on the wire.

Async

The same surface, for FastAPI and anything else on asyncio.

from noria_notify import AsyncNotify

async with AsyncNotify(api_key=key, base_url=url) as notify:
    await notify.emails.send({"to": "a@example.com", "subject": "Hi", "text": "there"})
    await notify.sms.send({"to": "0712345678", "text": "Your code is 482913"})

Sending

notify.emails.send(email, idempotency_key=f"signin-{token.id}")
notify.emails.send_batch([email, email])

notify.sms.send(message, idempotency_key=f"otp-{token.id}")
notify.sms.send_batch([message, message])

Both also take template and variables instead of a body, scheduled_at for a future send and tags. Email adds attachments as base64, plus cc, bcc, reply_to and headers.

An SMS takes one recipient, because one row carries one provider message id and that is what a delivery receipt is matched against. Use send_batch for many.

Reading what was sent

One ledger covers every channel:

notify.messages.list(channel="sms", status="failed", limit=50)
notify.messages.get(message_id)
notify.messages.events(message_id)
notify.messages.cancel(message_id)
notify.messages.requeue(message_id)

Domains, senders, templates, suppressions, webhooks

notify.domains.create("norialabs.com")          # returns the DNS records to publish
notify.senders.create("NORIA")                  # registered pending approval
notify.templates.upsert("welcome", subject="Hi {{name}}", html=html)
notify.templates.upsert("otp", channel="sms", text="Code {{code}}")
notify.suppressions.add("0712345678", channel="sms", reason="unsubscribe")
notify.is_suppressed("0712345678", "sms")
notify.webhooks.create("https://app.example.com/hooks/notify", ["delivered", "bounced"])

A slug is unique per channel, so an otp email template and an otp SMS template can coexist.

Errors

Failures raise NotifyError with code, status, details and request_id, plus suppressed, over_quota and retryable for the common branches. The client retries 408, 429 and 5xx with backoff and never retries quota_exceeded, suppressed_recipient, domain_not_verified, sender_not_approved or message_too_long.

try:
    notify.sms.send(message)
except NotifyError as error:
    if error.suppressed:
        return
    raise

Webhooks

from noria_notify import verify_webhook

event = verify_webhook(
    payload=await request.body(),
    signature=request.headers["noria-signature"],
    secret=os.environ["NORIA_NOTIFY_WEBHOOK_SECRET"],
)

HMAC-SHA256 over <timestamp>.<raw body>, compared in constant time, with a five minute tolerance against replay. event["data"]["channel"] says which channel the event came from.

Tests

uv sync
uv run pytest
uv run ruff check src tests

Release files for norialabs-notify 0.1.0

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for norialabs-notify 0.1.0
File Size Uploaded
norialabs_notify-0.1.0.tar.gz 7.1 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for norialabs-notify 0.1.0
File Interpreter ABI Platform
norialabs_notify-0.1.0-py3-none-any.whl Python 3 none any Details

Total release size: 15.3 kB

Release files / norialabs_notify-0.1.0.tar.gz

Download URL norialabs_notify-0.1.0.tar.gz
Size 7.1 kB
Tags Source
SHA-256 checksum
How to use checksums
78f3f1df93597f4240ebbbb8e3b61fdb3a396bc20505a989928bce131d8a528c
BLAKE2b-256 checksum
How to use checksums
82dc8df50e559e812ab11be189faefe64161c8de07c7b33aa1038c319e21c21a
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Sep 21, 2026.

Transparency log

Release files / norialabs_notify-0.1.0-py3-none-any.whl

Download URL norialabs_notify-0.1.0-py3-none-any.whl
Size 8.1 kB
Tags Python 3
SHA-256 checksum
How to use checksums
aa0ca003f2a65981a8d97d28712886c29d6d75b141602975ecf8536239598e51
BLAKE2b-256 checksum
How to use checksums
59128d20e8494aece947dcc82ffe642460898d4d74ac02a6fca949e2076fa399
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Sep 21, 2026.

Transparency log

Release history Release notifications | RSS feed

This release

0.1.0 This release

2 release files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page