REST API¶
The plugin exposes seven endpoints under NetBox's plugin API namespace. They follow NetBox conventions throughout: token authentication, limit/offset pagination, ?brief=1 for compact representations, and the same filter grammar as core.
The REST API is the primary interface. External parsers write events through it, and the outbound delivery loop reads and updates prepared notifications through it.
Base URL and authentication¶
Every request needs a NetBox API token:
The browsable API is available at the same URLs in a logged-in browser session, and the OpenAPI schema NetBox generates at /api/schema/ includes these endpoints.
Endpoints¶
| Endpoint | Model | Methods |
|---|---|---|
maintenance/ |
Maintenance |
GET, POST, PUT, PATCH, DELETE |
outage/ |
Outage |
GET, POST, PUT, PATCH, DELETE |
impact/ |
Impact |
GET, POST, PUT, PATCH, DELETE |
eventnotification/ |
EventNotification |
GET, POST, PUT, PATCH, DELETE |
notification-templates/ |
NotificationTemplate |
GET, POST, PUT, PATCH, DELETE |
prepared-notifications/ |
PreparedNotification |
GET, POST, PUT, PATCH, DELETE |
sent-notifications/ |
SentNotification |
GET only |
Note the inconsistency in naming: the event endpoints are singular (maintenance, outage, impact, eventnotification) and the messaging endpoints are plural and hyphenated. This is historical and is not going to change without a major version.
Maintenance¶
GET /api/plugins/notices/maintenance/
POST /api/plugins/notices/maintenance/
GET /api/plugins/notices/maintenance/{id}/
PATCH /api/plugins/notices/maintenance/{id}/
DELETE /api/plugins/notices/maintenance/{id}/
Fields¶
| Field | Type | Notes |
|---|---|---|
name |
string | Provider event ID. Required. |
summary |
string | Required. |
provider |
nested Provider | Write by PK. Required. |
start |
datetime | Required. |
end |
datetime | Required. Must not precede start. |
status |
string | See Maintenance Events. Required. |
status_color |
string | Read-only. Badge colour for the current status. |
original_timezone |
string | IANA timezone name from the provider notification. |
internal_ticket |
string | Your change reference. |
acknowledged |
boolean | |
replaces |
integer | PK of the maintenance this one replaces. |
impact |
string | Free-text impact description, distinct from Impact records. |
comments |
string | |
impact_count |
integer | Read-only count of linked Impact records. |
tags, custom_fields, created, last_updated |
Standard NetBox fields. |
Setting replaces on creation moves the replaced maintenance to RE-SCHEDULED through a signal. Nothing in the request body reflects that; re-read the other event to see it.
The response also carries two read-only lists: impacts, the event's linked Impact records in their nested form (event, target, impact level), and notifications, the event's received EventNotification records without the raw email or body. Both are present on maintenance and outage responses. To filter or page through an event's impacts, query the impact endpoint by event, as shown below.
Filters¶
id, name, summary, status, provider_id, start, end, original_timezone, internal_ticket, acknowledged, impact, comments.
Plus relational filters:
| Filter | Effect |
|---|---|
replaces_id |
Maintenances replacing a given event |
has_replaces |
true for events that have been rescheduled, false for those that have not |
site_id |
Maintenances impacting anything in a site |
region_id |
Same, rolled up by region |
site_group_id |
Same, rolled up by site group |
location_id |
Same, by location |
The four site filters traverse the cached Impact.sites and Impact.locations M2Ms. A target type with no registered resolver contributes nothing to them; see Site and Location Resolvers.
q searches name, summary, internal_ticket and the free-text impact field.
Example¶
POST /api/plugins/notices/maintenance/
Authorization: Token YOUR_API_TOKEN
Content-Type: application/json
{
"name": "PWIC-12345",
"summary": "Planned fibre splice, Route 7",
"provider": 7,
"status": "CONFIRMED",
"start": "2026-09-14T02:00:00Z",
"end": "2026-09-14T06:00:00Z",
"original_timezone": "America/Toronto",
"internal_ticket": "CHG-4471"
}
Outage¶
GET /api/plugins/notices/outage/
POST /api/plugins/notices/outage/
GET /api/plugins/notices/outage/{id}/
PATCH /api/plugins/notices/outage/{id}/
DELETE /api/plugins/notices/outage/{id}/
Same shape as Maintenance, with end optional and three extra fields: reported_at, estimated_time_to_repair, and a status from the outage set. end becomes required once status is RESOLVED.
Filters match Maintenance minus the reschedule ones, plus reported_at and estimated_time_to_repair. The four site filters behave identically.
See Outage Events for worked examples.
Impact¶
GET /api/plugins/notices/impact/
POST /api/plugins/notices/impact/
GET /api/plugins/notices/impact/{id}/
PATCH /api/plugins/notices/impact/{id}/
DELETE /api/plugins/notices/impact/{id}/
Both ends of an Impact are generic foreign keys, so writes take a content type and an object ID for each.
| Field | Type | Notes |
|---|---|---|
event_content_type |
integer | ContentType PK. Limited to notices.maintenance and notices.outage. |
event_object_id |
integer | PK of the event. |
event |
object | Read-only. Nested maintenance or outage, whichever applies. |
target_content_type |
integer | ContentType PK. Must be in allowed_content_types. |
target_object_id |
integer | PK of the affected object. |
target |
object | Read-only. Full circuit representation for circuits, a basic {id, name, type} otherwise. |
impact |
string | NO-IMPACT, REDUCED-REDUNDANCY, DEGRADED or OUTAGE. Nullable. |
The write fields take numeric ContentType PKs, which differ between deployments. Look them up first:
GET /api/extras/content-types/?app_label=notices&model=maintenance
GET /api/extras/content-types/?app_label=dcim&model=device
Filters accept the dotted form instead, which is easier to use by hand. event_content_type and target_content_type are ContentTypeFilter fields, so this works:
Use it when you need the full Impact representation or impact filters; the impacts list on the event response carries only the nested form.
Other filters: id, event_object_id, target_object_id, impact, site_id, region_id, site_group_id, location_id.
Three validation rules apply on write, all from Impact.clean():
target_content_typemust appear inallowed_content_types.event_content_typemust benotices.maintenanceornotices.outage.- The parent event must not be closed. Impacts cannot be created or changed on a maintenance in
COMPLETEDorCANCELLED, or an outage inRESOLVED.
A unique_together constraint on the four generic FK columns prevents the same event being linked to the same target twice.
Event notifications¶
GET /api/plugins/notices/eventnotification/
POST /api/plugins/notices/eventnotification/
GET /api/plugins/notices/eventnotification/{id}/
DELETE /api/plugins/notices/eventnotification/{id}/
Stores the provider email that produced an event. This is the endpoint a parser posts to after creating the event itself.
| Field | Type | Notes |
|---|---|---|
event_content_type |
integer | ContentType PK, maintenance or outage. |
event_object_id |
integer | PK of the event. |
event |
object | Read-only nested event. |
subject |
string | Max 100 characters. |
email_from |
||
email_received |
datetime | |
email_body |
string | Rendered on the detail page after sanitisation. |
The model also has a binary email field holding the raw MIME message. It is not exposed through the serializer.
Notifications belong to their event: deleting a maintenance or outage, through the API or the UI, deletes its notifications along with its impacts. The UI delete confirmation lists them before you confirm.
Filters: id, event_content_type, event_object_id, subject, email_from, email_received, email_body. q searches subject, body and sender.
Notification templates¶
GET /api/plugins/notices/notification-templates/
POST /api/plugins/notices/notification-templates/
GET /api/plugins/notices/notification-templates/{id}/
PATCH /api/plugins/notices/notification-templates/{id}/
DELETE /api/plugins/notices/notification-templates/{id}/
Field reference is in Templates. Two API-specific behaviours:
headers_template accepts YAML. Send a YAML string and the serializer parses it into the stored JSON object. Sending a JSON object directly also works.
{
"headers_template": "X-Event-Reference: \"{{ maintenance.name }}\"\nReply-To: \"noc@example.com\""
}
scopes is writable inline. Scopes are nested in the template payload rather than having an endpoint of their own, and content_type there is the model name as a slug, not a PK:
{
"name": "Customer maintenance notice",
"slug": "customer-maintenance",
"event_type": "maintenance",
"granularity": "per_tenant",
"subject_template": "[{{ maintenance.status }}] Maintenance {{ maintenance.name }}",
"body_template": "...",
"scopes": [
{"content_type": "tenant", "object_id": null, "event_type": "maintenance", "weight": 1500}
]
}
A PATCH that includes scopes deletes every existing scope on the template and recreates the list from the payload. It is a replace, not a merge, so partial scope updates are not possible. Omit the key entirely to leave scopes alone.
contact_roles behaves the same way: supplying it replaces the whole set.
Filters: id, name, slug, event_type, granularity, is_base_template. event_type and granularity accept multiple values. q searches name, slug and description.
Prepared notifications¶
GET /api/plugins/notices/prepared-notifications/
POST /api/plugins/notices/prepared-notifications/
GET /api/plugins/notices/prepared-notifications/{id}/
PATCH /api/plugins/notices/prepared-notifications/{id}/
DELETE /api/plugins/notices/prepared-notifications/{id}/
This endpoint is the outbound queue, and status changes here are the only way to drive the state machine. The web UI does not expose the field.
| Field | Type | Notes |
|---|---|---|
template |
nested | Read-only. Write with template_id. |
template_id |
integer | Write-only. Required on create. |
event_content_type, event_id |
integer | Optional link to a maintenance or outage. |
status |
string | See below. |
contacts |
nested list | Read-only. Write with contact_ids. |
contact_ids |
list of integers | Write-only. |
recipients |
JSON | Read-only. Snapshot taken at approval. |
subject, body_text, body_html, headers, css, ical_content |
Rendered content. | |
approved_by, approved_at, sent_at, delivered_at, viewed_at |
Read-only. Set by the state machine. | |
message |
string | Write-only. Creates a journal entry for the transition. |
timestamp |
datetime | Write-only. Real time of the transition. |
Status rules¶
A create must use draft. Any other value is rejected:
{"status": ["A notification is always created as 'draft'. Create it first, then PATCH it to 'ready'."]}
An update must follow the transition graph. draft to ready to sent to delivered, with sent also able to reach failed, and failed able to return to ready. An invalid move names the valid ones:
The timestamp field lets a batching delivery system record when something actually happened rather than when it got round to reporting it. It cannot be in the future, sent_at cannot precede approved_at, and delivered_at cannot precede sent_at.
Read recipients, not contacts, when delivering. The full rationale, side effects and a polling loop are in Approval Workflow.
Filters: id, status, template_id, event_type, event_id, tenant_id, modified. status accepts multiple values. modified selects generated drafts whose content was edited since rendering (?modified=true). q searches subject and body text.
The read-only tenant and impact fields name the recipient group a generated notification belongs to, and modified is true for a hand-edited generated draft.
Generate notifications¶
POST /api/plugins/notices/maintenance/{id}/generate-notifications/
POST /api/plugins/notices/outage/{id}/generate-notifications/
Creates, updates and deletes draft notifications for the event. Body fields, both optional: templates (list of notification kind IDs; empty or absent means all applicable kinds; IDs that are not kinds for the event are rejected with 400) and dry_run (boolean, default false).
# Preview
curl -X POST -H "Authorization: Token $API_TOKEN" -H "Content-Type: application/json" \
-d '{"dry_run": true}' \
https://netbox.example.com/api/plugins/notices/maintenance/42/generate-notifications/
# Apply, for kinds 3 and 5 only
curl -X POST -H "Authorization: Token $API_TOKEN" -H "Content-Type: application/json" \
-d '{"templates": [3, 5]}' \
https://netbox.example.com/api/plugins/notices/maintenance/42/generate-notifications/
The response has a summary string, counts per action and one entry per recipient group in items:
{
"summary": "2 created",
"counts": {"create": 2},
"items": [
{"action": "create", "template": 3, "root_template": 3, "tenant": 7, "impact": null,
"contacts": [12, 15], "subject": "[CONFIRMED] Acme Maintenance: MAINT-1001",
"error": null, "notification": 91},
{"action": "create", "template": 3, "root_template": 3, "tenant": 8, "impact": null,
"contacts": [20], "subject": "[CONFIRMED] Globex Maintenance: MAINT-1001",
"error": null, "notification": 92}
]
}
A dry run words the summary as a plan ("2 to create, 1 failing") and returns notification: null for new drafts. An applied run is atomic: every write is checked against the caller's object permissions, and a violation returns 403 with nothing written. The caller needs add, change and delete on prepared notifications (otherwise 403) and view on the event (an event the caller cannot view returns 404). The actions are described in Outgoing Notifications.
Reset a draft¶
curl -X POST -H "Authorization: Token $API_TOKEN" \
https://netbox.example.com/api/plugins/notices/prepared-notifications/91/reset/
Re-renders a draft from its template and discards manual edits, returning the notification. Requires change on the notification. A notification that is not a draft, is not linked to an event, or fails to render returns 400.
Sent notifications¶
A read-only view of the same table, filtered by its manager to sent and delivered. Every field is read-only and only GET, HEAD and OPTIONS are accepted. It exists so an audit consumer can read the delivery log without any possibility of writing to it.
iCal feed¶
The calendar feed is not part of the REST API and is not a DRF endpoint, but it accepts the same tokens:
It supports a token query parameter in addition to the Authorization header, because calendar clients cannot set headers. It also honours ETag and If-None-Match. See Calendar and iCal Feed for parameters and caching behaviour.
GraphQL¶
Read-only GraphQL queries are registered for maintenance, outage, impact, event notification, notification template and prepared notification, in both single and list form, at NetBox's usual /graphql/ endpoint.
There are no GraphQL mutations. Writes go through REST.
Permissions¶
Endpoints enforce the standard NetBox model permissions, notices.view_maintenance, notices.add_impact and so on, including object-level constraints. sent-notifications/ checks notices.view_preparednotification, since the proxy shares the underlying model.
See Permissions for the full matrix and recommended role bindings.