diff --git a/apprise_api/api/templates/welcome.html b/apprise_api/api/templates/welcome.html
index bc80f0e..b242300 100644
--- a/apprise_api/api/templates/welcome.html
+++ b/apprise_api/api/templates/welcome.html
@@ -52,7 +52,7 @@
{% trans "Stateless Endpoints" %}
body |
- {% blocktrans %}Defines the message body. This field is required!{% endblocktrans %} |
+ {% blocktrans %}Defines the message body. This field is required (unless body_not_required GET parameter is provided - only used with templated notifications).{% endblocktrans %} |
title |
@@ -453,7 +453,7 @@ {% trans "Persistent Store Endpoints" %}
body |
- {% blocktrans %}Defines the message body. This field is required!{% endblocktrans %} |
+ {% blocktrans %}Defines the message body. This field is required (unless body_not_required GET parameter is provided - only used with templated notifications).{% endblocktrans %} |
title |
diff --git a/apprise_api/api/tests/test_notify.py b/apprise_api/api/tests/test_notify.py
index 74ddeff..41ca2ac 100644
--- a/apprise_api/api/tests/test_notify.py
+++ b/apprise_api/api/tests/test_notify.py
@@ -1483,3 +1483,40 @@ def test_stateful_notify_recursion(self, mock_notify):
'/notify/{}'.format(key), data=form_data, **headers)
assert response.status_code == 400
assert mock_notify.call_count == 0
+
+
+ @mock.patch('apprise.Apprise.notify')
+ def test_notify_no_body(self, mock_notify):
+ """
+ Test sending a notification without a body (if supported)
+ """
+
+ key = "dummy"
+
+ # Add some content
+ response = self.client.post(
+ '/add/{}'.format(key),
+ {'urls': 'onesignal://template_id:account_id@app_key/target_player_id'})
+ assert response.status_code == 200
+
+ # Set our return value
+ mock_notify.return_value = True
+
+ # Expect to fail because body is not provided
+ response = self.client.post(
+ '/notify/{}'.format(key),
+ data=json.dumps({}),
+ content_type='application/json',
+ )
+ assert response.status_code == 400 and response.json() == {
+ "error": "Bad FORM Payload provided"
+ }
+
+ # This now succeeds because body is set to not required explicitly
+ response = self.client.post(
+ '/notify/{}?body_not_required=true'.format(key),
+ data=json.dumps({}),
+ content_type='application/json',
+ )
+ assert response.status_code == 200 and mock_notify.call_count == 1
+
diff --git a/apprise_api/api/tests/test_stateless_notify.py b/apprise_api/api/tests/test_stateless_notify.py
index db87416..e6eef43 100644
--- a/apprise_api/api/tests/test_stateless_notify.py
+++ b/apprise_api/api/tests/test_stateless_notify.py
@@ -757,3 +757,35 @@ def test_notify_with_filters(self, mock_send):
# nothing was changed
assert N_MGR['json'].enabled is True
+
+ @mock.patch('apprise.Apprise.notify')
+ def test_notify_no_body(self, mock_notify):
+ """
+ Test sending a notification without a body (if supported)
+ """
+
+ # Set our return value
+ mock_notify.return_value = True
+
+ # Prepare our JSON data
+ json_data = {
+ 'urls': 'onesignal://template_id:account_id@app_key/target_player_id',
+ }
+
+ # Expect to fail because body is not provided
+ response = self.client.post(
+ '/notify/',
+ data=json.dumps(json_data),
+ content_type='application/json',
+ )
+ assert response.status_code == 400 and response.json() == {
+ "error": "Payload lacks minimum requirements"
+ }
+
+ # This now succeeds because body is set to not required explicitly
+ response = self.client.post(
+ '/notify/?body_not_required=true',
+ data=json.dumps(json_data),
+ content_type='application/json',
+ )
+ assert response.status_code == 200 and mock_notify.call_count == 1
diff --git a/apprise_api/api/views.py b/apprise_api/api/views.py
index e286b9b..bda4e4b 100644
--- a/apprise_api/api/views.py
+++ b/apprise_api/api/views.py
@@ -707,6 +707,9 @@ def post(self, request, key):
# rules
rules = {k[1:]: v for k, v in request.GET.items() if k[0] == ':'}
+ body_not_required = request.GET.get('body_not_required') == 'true'
+ title_not_required = request.GET.get('title_not_required') == 'true'
+
# our content
content = {}
if not json_payload:
@@ -761,7 +764,7 @@ def post(self, request, key):
'error': msg,
}, encoder=JSONEncoder, safe=False, status=status)
- if not content:
+ if not content and not body_not_required:
# We could not handle the Content-Type
logger.warning(
'NOTIFY - %s - Invalid FORM Payload provided using KEY: %s',
@@ -912,7 +915,7 @@ def post(self, request, key):
content['title'] = request.GET['title']
# Some basic error checking
- if not content.get('body') and not attach or \
+ if (not content.get('body') and not body_not_required and not attach) or \
content.get('type', apprise.NotifyType.INFO) \
not in apprise.NOTIFY_TYPES:
@@ -1124,12 +1127,15 @@ def post(self, request, key):
if content_type == 'text/html' else \
settings.LOGGING['formatters']['standard']['format']
+ notif_body = content.get('body') if not body_not_required else apprise.NOT_REQUIRED
+ notif_title = content.get('title', '') if not title_not_required else apprise.NOT_REQUIRED
+
# Now specify our format (and over-ride the default):
with apprise.LogCapture(level=level, fmt=fmt) as logs:
# Perform our notification at this point
result = a_obj.notify(
- content.get('body'),
- title=content.get('title', ''),
+ notif_body,
+ title=notif_title,
notify_type=content.get('type', apprise.NotifyType.INFO),
tag=content.get('tag'),
attach=attach,
@@ -1230,6 +1236,9 @@ def post(self, request):
# rules
rules = {k[1:]: v for k, v in request.GET.items() if k[0] == ':'}
+ body_not_required = request.GET.get('body_not_required') == 'true'
+ title_not_required = request.GET.get('title_not_required') == 'true'
+
# our content
content = {}
if not json_payload:
@@ -1324,7 +1333,7 @@ def post(self, request):
content['title'] = request.GET['title']
# Some basic error checking
- if not content.get('body') or \
+ if (not content.get('body') and not body_not_required) or \
content.get('type', apprise.NotifyType.INFO) \
not in apprise.NOTIFY_TYPES:
@@ -1492,14 +1501,17 @@ def post(self, request):
elif level == 'TRACE':
level = logging.DEBUG - 1
+ notif_body = content.get('body') if not body_not_required else apprise.NOT_REQUIRED
+ notif_title = content.get('title', '') if not title_not_required else apprise.NOT_REQUIRED
+
if settings.APPRISE_WEBHOOK_URL:
esc = ''
fmt = f'["%(levelname)s","%(asctime)s","{esc}%(message)s{esc}"]'
with apprise.LogCapture(level=level, fmt=fmt) as logs:
# Perform our notification at this point
result = a_obj.notify(
- content.get('body'),
- title=content.get('title', ''),
+ notif_body,
+ title=notif_title,
notify_type=content.get('type', apprise.NotifyType.INFO),
tag='all',
attach=attach,
@@ -1527,8 +1539,8 @@ def post(self, request):
else:
# Perform our notification at this point
result = a_obj.notify(
- content.get('body'),
- title=content.get('title', ''),
+ notif_body,
+ title=notif_title,
notify_type=content.get('type', apprise.NotifyType.INFO),
tag='all',
attach=attach,
diff --git a/requirements.txt b/requirements.txt
index 5f78d9f..3b678db 100644
--- a/requirements.txt
+++ b/requirements.txt
@@ -10,7 +10,7 @@
# apprise @ git+https://github.com/caronc/apprise@custom-tag-or-version
## 3. The below grabs our stable version (generally the best choice):
-apprise == 1.8.0
+apprise == 1.9.0
## Apprise API Minimum Requirements
django