API Usage
This guide covers the core functionality of the Apprise API: sending notifications. You can send notifications in two ways:
- Stateless: You provide the configuration URLs in the request payload.
- Stateful: You reference a pre-saved configuration Key.
Response Formats
Section titled “Response Formats”By default, endpoints return text/plain. If you prefer JSON, send Accept: application/json.
For notification endpoints (/notify and /notify/{KEY}), you can also request a simple HTML log view by sending Accept: text/html. This is mainly useful for human testing in a browser.
Stateless Notifications
Section titled “Stateless Notifications”Stateless notifications are ideal for “sidecar” usage where you don’t want to manage persistent configuration on the server. You must provide the urls parameter in every request.
Basic JSON Request
Section titled “Basic JSON Request”The most common method is sending a JSON payload to /notify.
Endpoint: POST /notify
curl -X POST \ -H "Content-Type: application/json" \ -d '{ "urls": "mailto://user:pass@gmail.com", "body": "Backup completed successfully.", "title": "System Status" }' \ http://localhost:8000/notifyFile Attachments
Section titled “File Attachments”To send attachments, use multipart/form-data. The API accepts standard file uploads or remote URLs. Use attach as the recommended field name. The aliases attachment and attachments are also accepted.
For JSON payloads that supply remote or encoded attachment values, use the same field names.
# Upload a local filecurl -X POST \ -F "urls=discord://webhook_id/token" \ -F "body=See attached log." \ -F "attach=@/var/log/syslog" \ http://localhost:8000/notify
# Reference a remote URL (Apprise will download and forward it)curl -X POST \ -F "urls=discord://webhook_id/token" \ -F "body=Security Camera Snapshot" \ -F "attach=http://camera-ip/snapshot.jpg" \ http://localhost:8000/notifyStateful Notifications
Section titled “Stateful Notifications”Stateful notifications allow you to simplify your client code. You store the complex URLs on the server once, assigned to a Key, and your client simply references that Key.
Endpoint: POST /notify/{KEY}
Sending to a Key
Section titled “Sending to a Key”If you have saved a configuration under the key my-alerts:
curl -X POST \ -H "Content-Type: application/json" \ -d '{ "body": "Database connection failed", "type": "warning" }' \ http://localhost:8000/notify/my-alertsTagging
Section titled “Tagging”Stateful configurations support tagging, allowing you to notify specific subgroups of your saved URLs.
The value passed in the tag field follows these rules:
tag value | Selected services |
|---|---|
"TagA" | Has TagA |
"TagA TagB" | Has TagA AND TagB |
"TagA+TagB" | Has TagA AND TagB |
"TagA&TagB" | Has TagA AND TagB |
"TagA,TagB" | Has TagA OR TagB |
"TagA|TagB" | Has TagA OR TagB |
"TagA TagC,TagB" | Has (TagA AND TagC) OR TagB |
# Notify services tagged "devops" OR "admin"curl -X POST -d '{"tag": "devops,admin", "body": "..."}' ...
# Notify services tagged "devops" AND "critical"curl -X POST -d '{"tag": "devops critical", "body": "..."}' ...
# Notify services matching ('comment' AND 'create') OR 'admin'curl -X POST -d '{"tag": "comment create,admin", "body": "..."}' ...Payload Mapping (Hooks)
Section titled “Payload Mapping (Hooks)”Sometimes you cannot change the payload format sent by a third-party tool (e.g., Grafana, Prometheus). Apprise API allows you to map incoming fields to Apprise-compatible fields using query parameters prefixed with a colon (:).
Mapping Rules
Section titled “Mapping Rules”| Syntax | Effect |
|---|---|
?:incoming_field=apprise_field | Rename incoming_field to the Apprise field |
?:incoming_field= | Remove incoming_field from the payload |
?:apprise_field=literal value | Hard-code apprise_field to a fixed string |
Example — your tool sends {"message": "Server Down", "severity": "high"}, but Apprise expects body and type:
curl -X POST \ -d '{"message": "Server Down", "severity": "high"}' \ "http://localhost:8000/notify/my-alerts?:message=body&:severity=type"Nested Field Mapping
Section titled “Nested Field Mapping”When the incoming payload uses nested objects or arrays, reference them on the source side of the rule using dot-notation for nested objects and bracket-notation for array elements. Both can be freely combined.
Nested Objects (Dot-Notation)
Section titled “Nested Objects (Dot-Notation)”Use a dot-separated path to walk into nested dictionaries:
# Payload from a monitoring tool:# { "event": { "title": "CPU spike", "state": "critical" },# "component": { "name": "web-server-01" } }curl -X POST \ -H "Content-Type: application/json" \ -d '{"event":{"title":"CPU spike","state":"critical"},"component":{"name":"web-server-01"}}' \ "http://localhost:8000/notify/my-alerts?:event.title=title&:event.state=type&:component.name=body"Array Elements (Bracket-Notation)
Section titled “Array Elements (Bracket-Notation)”Append [N] directly after the key name to dereference index N of that array. Multiple subscripts and mixing with dot-notation are both supported:
# Payload from a forum / project-management webhook (e.g. Scoold / Para):# { "items": [ { "title": "New post", "objectURI": "https://example.com/q/1234" } ] }curl -g -X POST \ -H "Content-Type: application/json" \ -d '{"items":[{"title":"New post","objectURI":"https://example.com/q/1234"}]}' \ "http://localhost:8000/notify/my-alerts?:items[0].title=title&:items[0].objectURI=body"Chained subscripts traverse nested arrays:
# Source key What it resolves toitems[0] first element of the items arrayitems[0].objectURI .objectURI of the first elementmatrix[0][1] row 0, column 1 of a 2-D arraya[0][1][2].value[3] nested arrays -> dict -> arrayRules and limits:
- Only the source side may use path notation; the target must be a flat Apprise field (
title,body,type,format,tag, etc.). Nin[N]must be a non-negative integer. Bothkey[abc](non-integer) andkey[0/key0](unmatched bracket) are rejected with aWARNING.- If any step in the path cannot be resolved (missing key, index out of range, or a non-list node encountered at an index step), the server returns 400 and logs a
WARNING— no notification is sent. This lets you catch misconfigured rules without silently dropping messages. - Depth is counted as the total number of individual traversal operations — each dict-key lookup and each array-index dereference counts as one step. For example,
items[0].objectURIis 3 steps (items->[0]->objectURI), anda[0][1][2].b[3]is 6 steps. - The maximum traversal depth defaults to 5. Adjust it with the
APPRISE_WEBHOOK_MAPPING_MAX_DEPTHenvironment variable.
Questions or Feedback?
Documentation
Notice a typo or an error? Report it or contribute a fix .
Technical Issues
Having trouble with the code? Open an issue on GitHub: