Skip to main content

cloudme-notify-django

Django integration for the CloudMe Notify API, built on the cloudme-notify SDK: settings-driven configuration, a ready notify client, token caching in Django's cache, a webhook view with signature verification, and a notify_doctor command.

Requirements

  • Python 3.9+, Django 4.2+
  • A Notify API client: client_id, api_key and its RSA private key (dashboard -> API clients)

Installation

pip install cloudme-notify-django
# settings.py
INSTALLED_APPS = [
    # ...
    "cloudme_notify_django",
]

NOTIFY = {
    "ENVIRONMENT": "sandbox",                          # "production" for real sends
    "CLIENT_ID": os.environ["NOTIFY_CLIENT_ID"],
    "API_KEY": os.environ["NOTIFY_API_KEY"],
    "PRIVATE_KEY_PATH": BASE_DIR / "secrets" / "notify.pem",
    "WEBHOOK_SECRET": os.environ.get("NOTIFY_WEBHOOK_SECRET"),
}

Every key may be left out of NOTIFY when the matching environment variable is set (NOTIFY_ENVIRONMENT, NOTIFY_CLIENT_ID, NOTIFY_API_KEY, NOTIFY_PRIVATE_KEY_PATH, NOTIFY_WEBHOOK_SECRET, ...).

Key Default
ENVIRONMENT "sandbox" picks the API URL; must match the API client's environment
BASE_URL – only for a custom deployment; overrides ENVIRONMENT's URL
CLIENT_ID, API_KEY – required
PRIVATE_KEY_PATH / PRIVATE_KEY – path to the PEM file (preferred) or the PEM text
TOKEN_STORE "cache" "cache" (Django cache, shared by workers) or "memory"
CACHE_ALIAS, CACHE_PREFIX "default", "notify" which cache, and the key prefix
WEBHOOK_SECRET, WEBHOOK_TOLERANCE –, 300 to receive webhooks
LOCALE – language of error messages: uz, uz-Cyrl, ru, en
TIMEOUT, CONNECT_TIMEOUT, MAX_RETRIES 10, 5, 3 HTTP behaviour

Check the setup — nothing is sent and no secret is printed:

python manage.py notify_doctor

Sending

from cloudme_notify_django import notify

notify.sms.send(to="+998901234567", message="Buyurtmangiz tayyor")
notify.telegram.send(to=user.phone, template_id=42, variables={"name": user.first_name})
notify.email.send(to=user.email, subject="Hisob-faktura", message="...")

notify is the full cloudme-notify client (otp, templates, debts, scheduled_messages, balance(), message()...), created on first use and shared by the process. The access token lives in your Django cache, so web workers and Celery tasks reuse it instead of authenticating on every request.

# OTP
otp = notify.otp.send(to=request.POST["phone"])
request.session["otp_id"] = otp.otp_id
result = notify.otp.verify(request.session["otp_id"], request.POST["code"])

# Scheduled messages
from cloudme_notify import Recurrence

notify.scheduled_messages.create(
    channel="sms",
    starts_at=appointment.starts_at - timedelta(days=1),   # aware datetimes keep their offset
    recipient=customer.phone,
    message=f"Eslatma: ertaga soat {appointment.starts_at:%H:%M} da qabul",
)
notify.scheduled_messages.create(channel="telegram", starts_at="2026-10-05T09:00:00+05:00",
                                 contact_group_id=12, message="Salom, {{name}}!", recurrence=Recurrence.weekly([1, 4]))

# Debt collection
debt = notify.debts.create(name=invoice.customer.name, phone=invoice.customer.phone,
                           amount=invoice.total, due_date=invoice.due_date, external_id=f"INV-{invoice.pk}")

Errors are the SDK's exceptions (cloudme_notify.ValidationError, InsufficientBalanceError, ForbiddenError, ...), each with error_code and a localized hint.

Sending from a request blocks for the API round-trip; for bulk or non-urgent sends call it from a Celery task (or use scheduled messages).

Receiving webhooks

# urls.py
from cloudme_notify_django.webhooks import webhook_view

urlpatterns = [
    path("webhooks/notify/", webhook_view),
]
# yourapp/signals.py (imported from AppConfig.ready)
from django.dispatch import receiver
from cloudme_notify_django.signals import webhook_received

@receiver(webhook_received)
def on_notify_event(sender, event, **kwargs):
    # a retried delivery carries the same event.id - dedupe on it
    if event.event == "message.failed":
        Order.objects.filter(notify_message_id=event.message_id).update(sms_failed=True)

Register the URL as the API client's webhook in the Notify dashboard and put its signing secret in NOTIFY["WEBHOOK_SECRET"]. The view is CSRF-exempt, accepts only POST, verifies the signature and timestamp (401 otherwise) and answers 200 — keep receivers fast, Notify waits 5 seconds.

Prefer your own view? Decorate it:

from cloudme_notify_django.webhooks import notify_webhook

@notify_webhook
def notify_events(request):
    event = request.notify_event          # cloudme_notify.WebhookEvent
    ...
    return HttpResponse(status=204)

Testing

Swap the client for one on an httpx.MockTransport, so tests never call the API:

import httpx
from cloudme_notify import NotifyClient
from cloudme_notify_django import set_client

def handler(request):
    if request.url.path.endswith("/oauth/token"):
        return httpx.Response(200, json={"access_token": "t", "expires_in": 900, "refresh_token": "r"})
    return httpx.Response(200, json={"success": True, "message_id": "m-1", "status": "queued", "price": "0", "currency": "UZS"})

set_client(NotifyClient(client_id="c", api_key="k", private_key=TEST_PEM,
                        base_url=NotifyClient.SANDBOX_URL, transport=httpx.MockTransport(handler)))
# ... set_client(None) afterwards

Changing settings.NOTIFY (e.g. with override_settings) resets the client automatically.

License

MIT

Metadata

Release files for cloudme-notify-django 1.0.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 cloudme-notify-django 1.0.0
File Size Uploaded
cloudme_notify_django-1.0.0.tar.gz 15.4 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for cloudme-notify-django 1.0.0
File Interpreter ABI Platform
cloudme_notify_django-1.0.0-py3-none-any.whl Python 3 none any Details

Total release size: 28.6 kB

Release files / cloudme_notify_django-1.0.0.tar.gz

Download URL cloudme_notify_django-1.0.0.tar.gz
Size 15.4 kB
Tags Source
SHA-256 checksum
How to use checksums
a50e1645ab571b3772099f58b7c689448423a17bfa3c5ef59bf5d211f3319eb7
BLAKE2b-256 checksum
How to use checksums
ea3980406d3ed10a45e53fa6d1ed863977453a8c3b2e7295747b9a832b3e5d26
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/7.0.0 CPython/3.14.2

Release files / cloudme_notify_django-1.0.0-py3-none-any.whl

Download URL cloudme_notify_django-1.0.0-py3-none-any.whl
Size 13.2 kB
Tags Python 3
SHA-256 checksum
How to use checksums
53b69a8020d12b5bac77a72617f73d1dd7b4559a9da837f6ab7c75cb8895b47e
BLAKE2b-256 checksum
How to use checksums
ce03d69b42275b8d3237d10febb09e094c3ee4a987b3f720678f01293e721d5c
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/7.0.0 CPython/3.14.2

Release history Release notifications | RSS feed

This release

1.0.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