Skip to content

Conversation

@caronc
Copy link
Owner

@caronc caronc commented Aug 24, 2025

Description:

Related issue (if applicable): n/a

NotificationAPI Notifications

  • Source: https://www.notificationapi.com
  • Icon Support: Yes (icon URL passed via appImageUrl parameter)
  • Message Format: Plain Text and HTML
  • Message Limit: Varies by channel

NotificationAPI lets you trigger email, SMS, calls, push, and in‑app notifications using a single API. The Apprise plugin supports the US, CA, and EU regional hosts. Configure the content once in NotificationAPI, then trigger it from Apprise by sending the notification type and recipient information, with optional merge‑tag parameters.

🛠️ Setup Instructions

  1. Create a NotificationAPI account and sign in.
  2. In the dashboard, locate your clientId and clientSecret under Environments.
  3. Create or identify the notification type you want to trigger (for example, order_tracking).
  4. Make sure your recipients have the correct identifiers:
    • Email notifications require an email address on the to object.
    • SMS notifications require a phone number in E.164 format, for example +15005550006.
    • You can also address users by a NotificationAPI user id.
  5. If you are hosted outside the US, note your region’s API host (US default, CA, or EU).

✅ Apprise Support

Syntax

Valid URL forms:

  • napi://{ClientID}/{ClientSecret}/{Target}
  • napi://{Type}@{ClientID}/{ClientSecret}/{Target}
  • notificationapi://{...} (alias of napi://)

Targets can be combined in a single path and are grouped by a leading id. Each {Target} segment may be:

  • a user id (userid or @userid)
  • an email ([email protected])
  • an E.164 phone number (+15551234567)

Examples of grouped targets:

Parameter Breakdown

Variable Required Description
type No Notification type id from your NotificationAPI dashboard. Defaults to apprise.
mode No Notification mode; can be either message or template. Defaults to message.
id Yes* Client id. Required unless supplied in the path.
secret Yes* Client secret. Required unless supplied in the path.
to No Comma‑separated target; each subset of targets must have and id associated with them
region No us (default), ca, or eu to select the API host.
channels No Channels are detected based on first target detected. The following channels can be proivded: email, sms , inapp, web_push, mobile_push and/or slack.
from No Display name for the email From identity.
cc No Comma‑separated list of CC addresses.
bcc No Comma‑separated list of BCC addresses.
:{key} No Dynamic template parameter tokens passed to parameters (e.g., :orderId=123). It's important to prefix each one of these with a colon (:) for it to be correctly interpreted. This is only used if mode is set to template

* Required when not already set in the URL path component.

NotificationAPI Default Parameters

Each NotificationAPI request sent through Apprise includes the following default parameters:

Parameter Description
appBody The main message body payload of the notification.
appTitle The message title or subject line.
appType The Apprise notification type (e.g., info, success, warning, failure).
appId The Apprise application identifier, usually apprise.
appDescription The description text configured for the Apprise service.
appColor A colour code associated with the notification type (used by some channels for visual context).
appImageUrl A URL pointing to an icon image representing the notification type.
appUrl A URL reference back to the source application (if configured).

These parameters are always included by Apprise in addition to any custom :{key}={value} tokens you provide in your URL.

These defaults are common across all Apprise plugins, in addition to the service‑specific parameters described above.

📦 Examples

Send to one email recipient by type and let NotificationAPI pick the channel:

apprise -vv -t "Order Update" -b "Your order shipped."   napi://order_tracking@CLIENT_ID/CLIENT_SECRET/id/[email protected]

Send the same notification to multiple recipients using path segments:

apprise -vv -t "Status" -b "Processing complete."   napi://order_tracking@CLIENT_ID/CLIENT_SECRET/\
     id/[email protected]/+15552341234/alice_123

Force the SMS channel and set the region to Canada:

apprise -vv -t "Code" -b "Your verification code is 123456"   'napi://order_tracking@CLIENT_ID/CLIENT_SECRET/id/+16475550123?channel=sms&region=ca'

Set From, CC and BCC for an email:

apprise -vv -t "Release" -b "v2.0.1 is live."   'napi://release_note@CLIENT_ID/CLIENT_SECRET/id/[email protected]?from=Dev%20Team&[email protected]&[email protected]'

Pass dynamic template tokens that your NotificationAPI template references:

apprise -vv -t "Order" -b " "   'napi://order_tracking@CLIENT_ID/CLIENT_SECRET/[email protected]?:orderId=12345&:status=shipped'

Use a query‑only form, handy in YAML:

apprise -vv -t "Hello" -b "Hi there"   'napi://?id=CLIENT_ID&secret=CLIENT_SECRET&type=greeting&to=id,[email protected]'

Minimal (id + email):

apprise -vv -t "Welcome" -b "Hello from Apprise"   "napi://welcome_email@CID/SECRET/user123/[email protected]"

EU region + token substitutions

apprise -vv -b "<b>Your order shipped!</b>" --format=html   "napi://order_update@CID/SECRET/user123/[email protected]?region=eu&:firstName=Chris&:trackingUrl=https://t.example/ABC123"

Setting From / CC / BCC / Reply‑To (email)

apprise -vv -b "Body"   "napi://newsletter@CID/SECRET/user123/[email protected]?from=Team<[email protected]>&[email protected]&[email protected]&[email protected]"

New Service Completion Status

  • apprise/plugins/notificationapi.py
  • pypackage.toml update keywords section to include notificationapi
  • README.md table entry
  • packaging/redhat/python-apprise.spec, update %global common_description

Checklist

  • The code change is tested and works locally.
  • There is no commented out code in this PR.
  • No lint errors (run tox -e lint and tox -e format).
  • Test coverage added (use tox -e minimal).

Testing

Anyone can help test this source code as follows:

# Create a virtual environment
python3 -m venv apprise && cd apprise
source bin/activate

# Install this branch
pip install git+https://github.com/caronc/apprise.git@notification-api-support

# Exercise the plugin
apprise -t "Test Title" -b "Test Message"   'napi://apprise@CLIENT_ID/CLIENT_SECRET/id/[email protected]'

@codecov
Copy link

codecov bot commented Aug 24, 2025

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 99.65%. Comparing base (74d5ad2) to head (8b8d026).

Additional details and impacted files
@@           Coverage Diff            @@
##           master    #1399    +/-   ##
========================================
  Coverage   99.65%   99.65%            
========================================
  Files         174      175     +1     
  Lines       22554    22886   +332     
  Branches     3587     3664    +77     
========================================
+ Hits        22476    22808   +332     
  Misses         70       70            
  Partials        8        8            
Files with missing lines Coverage Δ
apprise/config/base.py 100.00% <ø> (ø)
apprise/plugins/notificationapi.py 100.00% <100.00%> (ø)
apprise/utils/parse.py 100.00% <100.00%> (ø)
🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

@caronc caronc force-pushed the notification-api-support branch from 80dde3c to 5f34b60 Compare August 26, 2025 01:55
Copy link

@sahandseifi sahandseifi left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks fantastic and very close to v1. Added a few comments about rethinking the API call, from there we should be able to take over.

continue

# Store our content
uid, recipient = result.group("id"), result.group("target")

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not sure if this is in line with your library's standards, but ideally, the user should be able to pass all 3 params at once: id, email, and number.

E.g.

apprise -t "Test Title" -b "Test Message"   'napi://...@.../.../id:xyz/email:[email protected]'

Or in another way that you see fit.

Copy link
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Got it... id is now somewhat of a delimiter. YOu can specify an email, id, and/or SMS # in any order you want... the second a second id is defined/detected, it sets up a new target that can be notified.

Also got 100% test coverage going too i this last push to this PR. Have a look at the test examples and let me know your thoughts (id:sms and id:email) are now just any combination of:

  • id/email
  • id/sms
  • id/sms/email
  • sms/email/id

The above equates to a single target, where as this would equate to 2:

  • id/email/id/email
  • id/sms/id/sms
  • id/sms/email/id/sms/email
  • sms/email/id/email/sms/id

Hopefully this makes sense

parameters = self.tokens.copy()

# Apply some defaults template values
parameters["appBody"] = body

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm thinking that with this library, it's probably expected by the user that we bypass our templating engine and like your other plugins, the content is directly sent over SMS or email with the title/body specified without a templating layer.

In this case, the "parameters" field in the API body is not used, and instead, the request should look like below, directly providing the content of each channel:

{
  type: 'order_tracking',
  to: {
    id: 'spongebob.squarepants',
    email: '[email protected]',
    number: '+15005550006'
  },
  email: {
    subject: 'Your Krabby Patty Order is Ready!',
    html: '<h1>Order Update</h1><p>Your order is ready for pickup!</p>',
    senderName: 'Krusty Krab', (optional)
    senderEmail: '[email protected]' (optional)
  },
  sms: {
    message: 'Your Krabby Patty order is ready for pickup!'
  }
  inapp: { title, image (optional), url (optional) },
  web_push: { title, message, icon (optional), url (optional) },
  mobile_push: { title, message },
  slack: { text, blocks (optional) }
}

E.g.

apprise -vv -t "title" -b "body" 'napi://....?channels=email,sms'

{
  type: 'order_tracking',
  to: {
    ...
  },
  email: {
    subject: "title",
    html: "body",
  },
  sms: {
    message: title + '\n' + body
  }
}

https://www.notificationapi.com/docs/reference/server#pass-content-directly-without-templates

Copy link
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I've switched the default to message while still leaving the template functionality in place; can be changed using new mode= flag.

}

if self.channel != NotificationAPIChannel.AUTO:
_payload["forceChannels"] = [self.channel]

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Feel free to remove/ignore all the other optional parameters such as forceChannels, options, cc, bcc, ...

Anything other than type, to, email/sms/... wouldn't be necessary for v1.

f"({entry}) is invalid.")
self.logger.warning(msg)

if channel is None:

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

With the next comment (below), this wouldn't be necessary.

@caronc caronc changed the title NotificationAPI Support - wip NotificationAPI Support Sep 6, 2025
@caronc caronc requested a review from Copilot October 10, 2025 22:35
Copy link

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull Request Overview

This PR adds support for NotificationAPI notifications to Apprise, enabling users to send notifications through email, SMS, calls, push, and in-app channels using a single API integration.

  • Adds new NotifyNotificationAPI plugin with comprehensive URL parsing and payload generation for both template and message modes
  • Extends URL schema length limits from 12 to 32 characters to accommodate "notificationapi" schema
  • Includes comprehensive test suite covering URL parsing, payload generation, and error handling

Reviewed Changes

Copilot reviewed 8 out of 8 changed files in this pull request and generated 2 comments.

Show a summary per file
File Description
apprise/plugins/notificationapi.py New plugin implementation for NotificationAPI service with support for multiple channels and modes
tests/test_plugin_notificationapi.py Comprehensive test suite covering URL validation, payload generation, and error scenarios
apprise/utils/parse.py Extended regex patterns to support longer schema names and added sort parameter to parse_list function
apprise/config/base.py Updated URL detection regex to support longer schema names
pyproject.toml Added NotificationAPI to keywords list for package discovery
packaging/redhat/python-apprise.spec Updated package description to include NotificationAPI
README.md Added NotificationAPI entry to supported services table
tests/helpers/rest.py Enhanced error reporting in test helper for better debugging

Tip: Customize your code reviews with copilot-instructions.md. Create the file or learn how to get started.

@caronc caronc force-pushed the notification-api-support branch from 3064eb0 to 084b098 Compare October 10, 2025 23:40
@caronc caronc requested a review from Copilot October 11, 2025 01:57
Copy link

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull Request Overview

Copilot reviewed 8 out of 8 changed files in this pull request and generated 8 comments.


Tip: Customize your code reviews with copilot-instructions.md. Create the file or learn how to get started.

body, title="", notify_type=NotifyType.INFO, **kwargs):
# Perform our post
self.logger.debug(
"NotifiationAPI POST URL: {} (cert_verify={!r})".format(
Copy link

Copilot AI Oct 11, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Corrected spelling of 'NotifiationAPI' to 'NotificationAPI'.

Copilot uses AI. Check for mistakes.
@caronc caronc requested review from Copilot and sahandseifi October 25, 2025 15:41
Copy link

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull Request Overview

Copilot reviewed 8 out of 8 changed files in this pull request and generated 3 comments.


💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

"instance": TypeError,
}),
("napi://client_id/client_secret/id/g@rb@ge/+15551235553/", {
# g@rb@ge enry ignored
Copy link

Copilot AI Oct 25, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Corrected spelling of 'enry' to 'entry'.

Suggested change
# g@rb@ge enry ignored
# g@rb@ge entry ignored

Copilot uses AI. Check for mistakes.
def parse_list(*args, cast=None, allow_whitespace=True):
def parse_list(*args, cast=None, allow_whitespace=True, sort=True):
"""Take a string list and break it into a delimited list of arguments. This
funciton also supports the processing of a list of delmited strings and
Copy link

Copilot AI Oct 25, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Corrected spelling of 'funciton' to 'function' and 'delmited' to 'delimited'.

Suggested change
funciton also supports the processing of a list of delmited strings and
function also supports the processing of a list of delimited strings and

Copilot uses AI. Check for mistakes.
def url_identifier(self):
"""
Returns all of the identifiers that make this URL unique from
another simliar one. Targets or end points should never be identified
Copy link

Copilot AI Oct 25, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Corrected spelling of 'simliar' to 'similar'.

Suggested change
another simliar one. Targets or end points should never be identified
another similar one. Targets or end points should never be identified

Copilot uses AI. Check for mistakes.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants