Skip to content

Inspection & Debugging

Apprise includes a small set of inspection utilities that let your application discover what is supported at runtime, and capture diagnostic output without taking over global logging configuration. The most important entry point is Apprise.details(), which is designed for dynamic tooling such as URL builders, setup wizards, admin UIs, and validation pipelines.

Inspecting Capabilities with Apprise.details()

Section titled “Inspecting Capabilities with Apprise.details()”

Apprise.details() returns a dictionary that describes:

  • the Apprise version your process is running,
  • the active AppriseAsset configuration (icons, themes, masks),
  • every schema (plugin) Apprise knows about, including URL construction templates and token metadata that can be used to build a correct Apprise URL dynamically.

The following will provide you a full dump of all of the information loaded into Apprise:

import json
import apprise
apobj = apprise.Apprise()
# Retrieve Details
info: dict = apobj.details()
# Print formatted JSON
print(json.dumps(info, indent=2))

Some schemas may be present but not usable because optional dependencies are missing. If you are building a UI, you usually want to surface these as “available, but currently disabled”, along with the reasons for it being disabled. Reasons can be that the server simply doesn’t have the nessisary packages installed to use it, environment related, or that it was manually disabled by the administrator. By default show_disabled=False, so to see this list, you need to explicitly set it.

import apprise
apobj = apprise.Apprise()
# Include services that exist but are not currently usable
info: dict = apobj.details(show_disabled=True)

At a high level, details() is meant to be JSON friendly. You can safely json.dumps() it (translations are normalized to strings before returning).

You should expect these top-level keys:

  • version: the Apprise library version.
  • asset: the active AppriseAsset settings.
  • schemas: a list of all supported schemas, one entry per plugin.

The asset content is produced by AppriseAsset.details() and currently contains:

  • app_id
  • app_desc
  • default_extension
  • theme
  • image_path_mask
  • image_url_mask
  • image_url_logo

These values matter for clients that want to render icons, show a branded header, or link to the correct logo and theme assets. More on this can be found here

Each schema entry describes one plugin, such as discord, mailto, slack, and so on. A schema entry typically contains:

  • a human friendly name (plugin service_name),
  • protocol information (secure and insecure variants),
  • URL building templates,
  • token metadata for URL components and query parameters,
  • dependency requirements (required vs recommended packages).

The “URL building” portion is generated by the plugin inspection helper apprise.plugins.details(plugin), which returns a structure with these keys:

  • templates
  • tokens
  • args
  • kwargs

templates is a list of URL patterns supported by the plugin, expressed with {token} placeholders. Templates are meant for display and tooling, for example:

  • show examples in your UI,
  • drive a “wizard” that asks only for tokens relevant to the selected template,
  • validate that a constructed URL matches at least one template.

Templates are sourced directly from each plugin’s templates attribute.

The most valuable part of the schema details is the token metadata. Apprise generates a consistent, predictable token structure, even when plugins omit optional fields. This normalization is handled internally so client tooling can rely on stable keys.

Tokens are divided into three groups:

  • tokens: path, host, auth, and other “core” pieces used to initialize the plugin.
  • args: query string arguments (normal plugin options).
  • kwargs: dynamic key/value arguments, where the user provides both the argument name and its value (used by a small number of services).

Apprise injects a schema token automatically, and it is always required. Its values come from the plugin’s secure_protocol and protocol settings. This is how tooling can show, for example, http and https variants where appropriate.

After normalization, you can typically rely on these fields being present:

KeyDescription
nameA human friendly label (defaults to the token key if missing).
map_toThe plugin argument this token maps to (defaults to the token key).
typeToken type (defaults to string). Supports list:* and choice:*.
requiredDefaults to False unless explicitly set.
privateDefaults to False unless explicitly set.
defaultMay be present (including auto-filled when only one choice exists).
valuesFor choice:* tokens and some other constrained inputs.
delimFor list:* tokens (defaults vary by context).
regexOptional validation regex, normalized into a (pattern, flags) pair.
groupFor list-alias tokens that map to multiple underlying fields.

Apprise assigns different default delimiters depending on where the token lives:

  • tokens (path-oriented lists) default to /
  • args and kwargs (query-oriented lists) default to , and space

This matters when your UI needs to present how to enter multiple recipients, channels, or targets.

If multiple token names map to the same underlying argument via map_to, Apprise can expose a group field for list types, so tooling can show that one list token acts as an alias for multiple related inputs.

Dependencies and install hints (requirements)

Section titled “Dependencies and install hints (requirements)”

Apprise can also return dependency details per plugin via the inspection helper apprise.plugins.requirements(plugin).

The structure includes:

  • details: a short human readable message, auto-filled if the plugin does not provide one.
  • packages_required: a list of required packages (pip requirement strings).
  • packages_recommended: a list of recommended packages (pip requirement strings).

This is the piece you want to show when a plugin appears in details(show_disabled=True) but cannot be enabled until dependencies are installed.

A practical approach that tends to work well:

  1. Let the user choose a service from schemas by service_name.
  2. Show the available templates for that service.
  3. Once a template is selected, identify every {token} in the template and prompt for those from tokens.
  4. Offer optional query configuration based on args (and kwargs only if present).
  5. Respect private=True by masking input fields and never echoing the value back.
  6. Respect type to render correct widgets:
    • bool: checkbox
    • choice:*: dropdown using values
    • int/float: numeric input with min/max when present
    • list:*: multi-entry input and show the delimiter guidance from delim

For programmatic debugging, Apprise includes LogCapture, a context manager that intercepts log output for the duration of a block. This is often preferable to globally modifying Python logging handlers.

import apprise
apobj = apprise.Apprise()
apobj.add("mailto://user:pass@example.com")
# Capture all logs (INFO and above) to memory
with apprise.LogCapture(level=apprise.logging.INFO) as output:
apobj.notify(title="Hello", body="World")
# 'output' is a StringIO object
print("Logs captured:")
print(output.getvalue())

You can also direct logs to a temporary or persistent file.

import apprise
apobj = apprise.Apprise()
with apprise.LogCapture(path="/var/log/apprise.log", delete=False) as fp:
apobj.notify(title="System", body="Crash detected")

You can inject custom formatting into the log stream to render logs directly into a web page.

import apprise
fmt = (
"<li>"
"<span class=\"time\">%(asctime)s</span> "
"<span class=\"lvl\">%(levelname)s</span> "
"<span class=\"msg\">%(message)s</span>"
"</li>"
)
apobj = apprise.Apprise()
with apprise.LogCapture(fmt=fmt) as logs:
apobj.notify(title="Test", body="Message")
# Get the HTML string
html: str = f"<ul>{logs.getvalue()}</ul>"

If you are integrating Apprise into another library or service:

  • Prefer details() for capability discovery and for building UI around supported plugins and their configuration fields.
  • Prefer LogCapture in tests and diagnostics so you can show users meaningful error output without forcing them to reconfigure logging globally.