Webhook Listeners — Nextcloud latest Administration Manual latest documentation

# Webhook Listeners

## Introduction

Nextcloud supports sending notifications to external services whenever something important happens, such as when files are changed or updated.

## Overview

The Webhook Listeners app enables your Nextcloud server to automatically notify external services whenever important events - such as file changes, uploads, or deletions - occur in your instance. By configuring webhook listeners, administrators can set up custom HTTP notifications (webhooks) that are triggered by specific internal events, allowing seamless integration with other platforms and automation of workflows without manual intervention.

The Webhook Listeners app enables your Nextcloud server to automatically notify external services whenever important events - such as file changes, uploads, or deletions - occur in your instance. By configuring webhook listeners, administrators can set up custom HTTP notifications (webhooks) that are triggered by specific internal events, allowing seamless integration with other platforms and automation of workflows without manual intervention.

The app works by monitoring Nextcloud’s event system and dispatching HTTP requests to defined endpoints whenever a matching event takes place. Management and configuration of webhook listeners are handled via the Nextcloud OCS API and command-line tools. The app is ideal for scenarios where you want to connect Nextcloud with notification systems, external automation platforms, or custom integrations - without requiring manual polling.

## Installation

Enable the `webhook_listeners` app that comes bundled with Nextcloud - e.g.

    occ app:enable webhook_listeners

## Listening to events

You can use the OCS API to add webhooks for specific events. See: Register a new webhook.

Note: When authenticating with the OCS API to register webhooks, the account you use must have administrator rights or delegated administrator rights.

### Filters

When registering a webhook listener, you can specify a filter parameter. The value of this parameter must be a JSON object whose properties represent filter conditions. The `{}` object is an empty query, meaning no specific criteria are set, so all events are matched.

If you want to match events triggered by a specific user, you can pass `{ "user.uid": "bob" }` to match all events associated with the user `bob`.

To enforce multiple criteria, simply pass multiple properties: `{ "event.tableId": 42, "event.rowId": 3 }`.

If you want to match values partially, you can use regular expressions: `{ "user.uid": "/admin_.*/" }` will match any user whose user ID starts with `admin_`. This can be especially useful for filesystem events when filtering by path: `{ "event.node.path": "/^\\/.*\\/files\\/Special folder\\//" }` will match files inside the `Special folder` of any user. Note that the slashes in the path need to be escaped with two backslashes: once because you are inside a JSON string, and once because you are inside a regular expression.

You can also use additional comparison operators (`$e`, `$ne`, `$gt`, `$gte`, `$lt`, `$lte`, `$in`, `$nin`) as well as logical operators (`$and`, `$or`, `$not`, `$nor`). For example, use `{ "time": { "$lt": 1711971024 } }` to accept only events prior to April 1st, 2024, and `{ "time": { "$not": { "$lt": 1711971024 } }}` to accept events after April 1st, 2024.

### Speeding up webhook dispatch

This app uses background jobs to trigger registered webhooks. By default, webhooks are triggered every 5 minutes, as the default cron interval is set to 5 minutes. To trigger webhooks sooner, you can set up a background job worker.

The following command will launch a worker for the webhook call background job:

#### Screen or tmux session

Run the following `occ` command inside a screen or tmux session, preferably four or more times, to enable parallel processing of multiple requests by different users or the same user. It is best to run one command per screen session or tmux window/pane to keep logs visible and make each worker easy to restart.

    set -e; while true; do sudo -E -u www-data php occ background-job:worker -v -t 60 "OCA\WebhookListeners\BackgroundJobs\WebhookCall"; done

For Nextcloud-AIO you should use this command on the host server.

    set -e; while true; do sudo docker exec -it nextcloud-aio-nextcloud docker exec -it nextcloud-aio-nextcloud sudo -E -u www-data php occ background-job:worker -v -t 60 "OCA\WebhookListeners\BackgroundJobs\WebhookCall"; done

You may want to adjust the number of workers and the timeout (in seconds) to your needs. The logs of the worker can be checked by attaching to the screen or tmux session.

#### Systemd service

1. Create a systemd service file in `/etc/systemd/system/nextcloud-webhook-worker@.service` with the following content:

    [Unit]
    Description=Nextcloud Webhook worker %i
    After=network.target

    [Service]
    ExecStart=/opt/nextcloud-webhook-worker/taskprocessing.sh %i
    Restart=always
    StartLimitInterval=60
    StartLimitBurst=10

    [Install]
    WantedBy=multi-user.target

2. Create a shell script in `/opt/nextcloud-webhook-worker/taskprocessing.sh` with the following content and make sure to make it executable:

    #!/bin/sh
    echo "Starting Nextcloud Webhook Worker $1"
    cd /path/to/nextcloud
    sudo -E -u www-data php occ background-job:worker -t 60 'OCA\WebhookListeners\BackgroundJobs\WebhookCall'

You may want to adjust the timeout to your needs (in seconds).

3. Enable and start the service 4 or more times:

    for i in {1..4}; do systemctl enable --now nextcloud-webhook-worker@$i.service; done

The status of the workers can be checked with (replace 1 with the worker number):

    systemctl status nextcloud-webhook-worker@1.service

The list of workers can be checked with:

    systemctl list-units --type=service | grep nextcloud-webhook-worker

The complete logs of the workers can be checked with (replace 1 with the worker number):

    sudo journalctl -xeu nextcloud-webhook-worker@1.service -f

It is recommended to restart this worker at least once a day to make sure code changes are effective and avoid memory leaks, in this example the service restarts every 60 seconds.

## Nextcloud Webhook Events

This is an exhaustive list of available events. It features the event ID and the available variables for filtering.

> - OCA\Forms\Events\FormSubmittedEvent

> >     array{
> >       "user": array {"uid": string, "displayName": string},
> >       "time": int,
> >       "event": array{
> >         "class": string,
> >         "form": array{
> >          "id": int,
> >          "hash": string,
> >      "title": string,
> >      "description": string,
> >      "ownerId": string,
> >      "fileId": string|null,
> >      "fileFormat": string|null,
> >      "created": int,
> >      "access": int,
> >      "expires": int,
> >      "isAnonymous": bool,
> >      "submitMultiple": bool,
> >      "showExpiration": bool,
> >      "lastUpdated": int,
> >      "submissionMessage": string|null,
> >      "state": int,
> >         },
> >         "submission": array{
> >           "id": int,
> >       "formId": int,
> >       "userId": string,
> >       "timestamp": int,
> >         },
> >       }
> >     }

> - OCA\Tables\Event\RowAddedEvent

> >     array{
> >       "user": array {"uid": string, "displayName": string},
> >       "time": int,
> >       "event": array{
> >         "class": string,
> >         "tableId": int,
> >         "rowId": int,
> >         "previousValues": null|array<int, mixed>,
> >         "values": null|array<int, mixed>
> >       }
> >     }

> - OCA\Tables\Event\RowDeletedEvent

> >     array{
> >       "user": array {"uid": string, "displayName": string},
> >       "time": int,
> >       "event": array{
> >         "class": string,
> >         "tableId": int,
> >         "rowId": int,
> >         "previousValues": null|array<int, mixed>,
> >         "values": null|array<int, mixed>
> >       }
> >     }

> - OCA\Tables\Event\RowUpdatedEvent

> >     array{
> >       "user": array {"uid": string, "displayName": string},
> >       "time": int,
> >       "event": array{
> >         "class": string,
> >         "tableId": int,
> >         "rowId": int,
> >         "previousValues": null|array<int, mixed>,
> >         "values": null|array<int, mixed>
> >       }
> >     }

> - OCP\Calendar\Events\CalendarObjectCreatedEvent

> >     array{
> >       "user": array {"uid": string, "displayName": string},
> >       "time": int,
> >       "event": array{
> >         "calendarId": int,
> >         "calendarData": array{
> >           "id": int,
> >           "uri": string,
> >           "{http://calendarserver.org/ns/}getctag": string,
> >           "{http://sabredav.org/ns}sync-token": int,
> >           "{urn:ietf:params:xml:ns:caldav}supported-calendar-component-set": 'Sabre\CalDAV\Xml\Property\SupportedCalendarComponentSet',
> >           "{urn:ietf:params:xml:ns:caldav}schedule-calendar-transp": 'Sabre\CalDAV\Xml\Property\ScheduleCalendarTransp'
> >           "{urn:ietf:params:xml:ns:caldav}calendar-timezone": string|null
> >         },
> >         "shares": list<array{
> >           "href": string,
> >           "commonName": string,
> >           "status": int,
> >           "readOnly": bool,
> >           "{http://owncloud.org/ns}principal": string,
> >           "{http://owncloud.org/ns}group-share": bool
> >         }>,
> >         "objectData": array{
> >           "id": int,
> >           "uri": string,
> >           "lastmodified": int,
> >           "etag": string,
> >           "calendarid": int,
> >           "size": int,
> >           "component": string|null,
> >           "classification": int
> >         }
> >       }
> >     }

> - OCP\Calendar\Events\CalendarObjectDeletedEvent

> >     array{
> >       "user": array {"uid": string, "displayName": string},
> >       "time": int,
> >       "event": array{
> >         "calendarId": int,
> >         "calendarData": array{
> >           "id": int,
> >           "uri": string,
> >           "{http://calendarserver.org/ns/}getctag": string,
> >           "{http://sabredav.org/ns}sync-token": int,
> >           "{urn:ietf:params:xml:ns:caldav}supported-calendar-component-set": 'Sabre\CalDAV\Xml\Property\SupportedCalendarComponentSet',
> >           "{urn:ietf:params:xml:ns:caldav}schedule-calendar-transp": 'Sabre\CalDAV\Xml\Property\ScheduleCalendarTransp'
> >           "{urn:ietf:params:xml:ns:caldav}calendar-timezone": string|null
> >         },
> >         "shares": list<array{
> >           "href": string,
> >           "commonName": string,
> >           "status": int,
> >           "readOnly": bool,
> >           "{http://owncloud.org/ns}principal": string,
> >           "{http://owncloud.org/ns}group-share": bool
> >         }>,
> >         "objectData": array{
> >           "id": int,
> >           "uri": string,
> >           "lastmodified": int,
> >           "etag": string,
> >           "calendarid": int,
> >           "size": int,
> >           "component": string|null,
> >           "classification": int
> >         }
> >       }
> >     }

> - OCP\Calendar\Events\CalendarObjectMovedEvent

> >     array{
> >       "user": array {"uid": string, "displayName": string},
> >       "time": int,
> >       "event": array{
> >         "sourceCalendarId": int,
> >         "sourceCalendarData": array{
> >           "id": int,
> >           "uri": string,
> >           "{http://calendarserver.org/ns/}getctag": string,
> >           "{http://sabredav.org/ns}sync-token": int,
> >           "{urn:ietf:params:xml:ns:caldav}supported-calendar-component-set": 'Sabre\CalDAV\Xml\Property\SupportedCalendarComponentSet',
> >           "{urn:ietf:params:xml:ns:caldav}schedule-calendar-transp": 'Sabre\CalDAV\Xml\Property\ScheduleCalendarTransp'
> >           "{urn:ietf:params:xml:ns:caldav}calendar-timezone": string|null
> >         },
> >         "targetCalendarId": int,
> >         "targetCalendarData": array{
> >           "id": int,
> >           "uri": string,
> >           "{http://calendarserver.org/ns/}getctag": string,
> >           "{http://sabredav.org/ns}sync-token": int,
> >           "{urn:ietf:params:xml:ns:caldav}supported-calendar-component-set": 'Sabre\CalDAV\Xml\Property\SupportedCalendarComponentSet',
> >           "{urn:ietf:params:xml:ns:caldav}schedule-calendar-transp": 'Sabre\CalDAV\Xml\Property\ScheduleCalendarTransp'
> >           "{urn:ietf:params:xml:ns:caldav}calendar-timezone": string|null
> >         },
> >         "sourceShares": list<array{
> >           "href": string,
> >           "commonName": string,
> >           "status": int,
> >           "readOnly": bool,
> >           "{http://owncloud.org/ns}principal": string,
> >           "{http://owncloud.org/ns}group-share": bool
> >         }>,
> >         "targetShares": list<array{
> >           "href": string,
> >           "commonName": string,
> >           "status": int,
> >           "readOnly": bool,
> >           "{http://owncloud.org/ns}principal": string,
> >           "{http://owncloud.org/ns}group-share": bool
> >         }>,
> >         "objectData": array{
> >           "id": int,
> >           "uri": string,
> >           "lastmodified": int,
> >           "etag": string,
> >           "calendarid": int,
> >           "size": int,
> >           "component": string|null,
> >           "classification": int
> >         }
> >       }
> >     }

> - OCP\Calendar\Events\CalendarObjectMovedToTrashEvent

> >     array{
> >       "user": array {"uid": string, "displayName": string},
> >       "time": int,
> >       "event": array{
> >         "calendarId": int,
> >         "calendarData": array{
> >           "id": int,
> >           "uri": string,
> >           "{http://calendarserver.org/ns/}getctag": string,
> >           "{http://sabredav.org/ns}sync-token": int,
> >           "{urn:ietf:params:xml:ns:caldav}supported-calendar-component-set": 'Sabre\CalDAV\Xml\Property\SupportedCalendarComponentSet',
> >           "{urn:ietf:params:xml:ns:caldav}schedule-calendar-transp": 'Sabre\CalDAV\Xml\Property\ScheduleCalendarTransp'
> >           "{urn:ietf:params:xml:ns:caldav}calendar-timezone": string|null
> >         },
> >         "shares": list<array{
> >           "href": string,
> >           "commonName": string,
> >           "status": int,
> >           "readOnly": bool,
> >           "{http://owncloud.org/ns}principal": string,
> >           "{http://owncloud.org/ns}group-share": bool
> >         }>,
> >         "objectData": array{
> >           "id": int,
> >           "uri": string,
> >           "lastmodified": int,
> >           "etag": string,
> >           "calendarid": int,
> >           "size": int,
> >           "component": string|null,
> >           "classification": int
> >         }
> >       }
> >     }

> - OCP\Calendar\Events\CalendarObjectRestoredEvent

> >     array{
> >       "user": array {"uid": string, "displayName": string},
> >       "time": int,
> >       "event": array{
> >         "calendarId": int,
> >         "calendarData": array{
> >           "id": int,
> >           "uri": string,
> >           "{http://calendarserver.org/ns/}getctag": string,
> >           "{http://sabredav.org/ns}sync-token": int,
> >           "{urn:ietf:params:xml:ns:caldav}supported-calendar-component-set": 'Sabre\CalDAV\Xml\Property\SupportedCalendarComponentSet',
> >           "{urn:ietf:params:xml:ns:caldav}schedule-calendar-transp": 'Sabre\CalDAV\Xml\Property\ScheduleCalendarTransp'
> >           "{urn:ietf:params:xml:ns:caldav}calendar-timezone": string|null
> >         },
> >         "shares": list<array{
> >           "href": string,
> >           "commonName": string,
> >           "status": int,
> >           "readOnly": bool,
> >           "{http://owncloud.org/ns}principal": string,
> >           "{http://owncloud.org/ns}group-share": bool
> >         }>,
> >         "objectData": array{
> >           "id": int,
> >           "uri": string,
> >           "lastmodified": int,
> >           "etag": string,
> >           "calendarid": int,
> >           "size": int,
> >           "component": string|null,
> >           "classification": int
> >         }
> >       }
> >     }

> - OCP\Calendar\Events\CalendarObjectUpdatedEvent

> >     array{
> >       "user": array {"uid": string, "displayName": string},
> >       "time": int,
> >       "event": array{
> >         "calendarId": int,
> >         "calendarData": array{
> >           "id": int,
> >           "uri": string,
> >           "{http://calendarserver.org/ns/}getctag": string,
> >           "{http://sabredav.org/ns}sync-token": int,
> >           "{urn:ietf:params:xml:ns:caldav}supported-calendar-component-set": 'Sabre\CalDAV\Xml\Property\SupportedCalendarComponentSet',
> >           "{urn:ietf:params:xml:ns:caldav}schedule-calendar-transp": 'Sabre\CalDAV\Xml\Property\ScheduleCalendarTransp'
> >           "{urn:ietf:params:xml:ns:caldav}calendar-timezone": string|null
> >         },
> >         "shares": list<array{
> >           "href": string,
> >           "commonName": string,
> >           "status": int,
> >           "readOnly": bool,
> >           "{http://owncloud.org/ns}principal": string,
> >           "{http://owncloud.org/ns}group-share": bool
> >         }>,
> >         "objectData": array{
> >           "id": int,
> >           "uri": string,
> >           "lastmodified": int,
> >           "etag": string,
> >           "calendarid": int,
> >           "size": int,
> >           "component": string|null,
> >           "classification": int
> >         }
> >       }
> >     }

> - OCP\Files\Events\Node\BeforeNodeCreatedEvent

> >     array{
> >       "user": array {"uid": string, "displayName": string},
> >       "time": int,
> >       "event": array{
> >         "class": string,
> >         "node": array{"id": string, "path": string}
> >       }
> >     }

> - OCP\Files\Events\Node\BeforeNodeTouchedEvent

> >     array{
> >       "user": array {"uid": string, "displayName": string},
> >       "time": int,
> >       "event": array{
> >         "class": string,
> >         "node": array{"id": string, "path": string}
> >       }
> >     }

> - OCP\Files\Events\Node\BeforeNodeWrittenEvent

> >     array{
> >       "user": array {"uid": string, "displayName": string},
> >       "time": int,
> >       "event": array{
> >         "class": string,
> >         "node": array{"id": string, "path": string}
> >       }
> >     }

> - OCP\Files\Events\Node\BeforeNodeReadEvent

> >     array{
> >       "user": array {"uid": string, "displayName": string},
> >       "time": int,
> >       "event": array{
> >         "class": string,
> >         "node": array{"id": string, "path": string}
> >       }
> >     }

> - OCP\Files\Events\Node\BeforeNodeDeletedEvent

> >     array{
> >       "user": array {"uid": string, "displayName": string},
> >       "time": int,
> >       "event": array{
> >         "class": string,
> >         "node": array{"id": string, "path": string}
> >       }
> >     }

> - OCP\Files\Events\Node\NodeCreatedEvent

> >     array{
> >       "user": array {"uid": string, "displayName": string},
> >       "time": int,
> >       "event": array{
> >         "class": string,
> >         "node": array{"id": string, "path": string}
> >       }
> >     }

> - OCP\Files\Events\Node\NodeTouchedEvent

> >     array{
> >       "user": array {"uid": string, "displayName": string},
> >       "time": int,
> >       "event": array{
> >         "class": string,
> >         "node": array{"id": string, "path": string}
> >       }
> >     }

> - OCP\Files\Events\Node\NodeWrittenEvent

> >     array{
> >       "user": array {"uid": string, "displayName": string},
> >       "time": int,
> >       "event": array{
> >         "class": string,
> >         "node": array{"id": string, "path": string}
> >       }
> >     }

> - OCP\Files\Events\Node\NodeDeletedEvent

> >     array{
> >       "user": array {"uid": string, "displayName": string},
> >       "time": int,
> >       "event": array{
> >         "class": string,
> >         "node": array{"id": string, "path": string}
> >       }
> >     }

> - OCP\Files\Events\Node\NodeCopiedEvent

> >     array{
> >       "user": array {"uid": string, "displayName": string},
> >       "time": int,
> >       "event": array{
> >         "class": string,
> >         "source": array{"id": string, "path": string}
> >         "target": array{"id": string, "path": string}
> >       }
> >     }

> - OCP\Files\Events\Node\NodeRestoredEvent

> >     array{
> >       "user": array {"uid": string, "displayName": string},
> >       "time": int,
> >       "event": array{
> >         "class": string,
> >         "source": array{"id": string, "path": string}
> >         "target": array{"id": string, "path": string}
> >       }
> >     }

> - OCP\Files\Events\Node\NodeRenamedEvent

> >     array{
> >       "user": array {"uid": string, "displayName": string},
> >       "time": int,
> >       "event": array{
> >         "class": string,
> >         "source": array{"id": string, "path": string}
> >         "target": array{"id": string, "path": string}
> >       }
> >     }

> - OCP\Files\Events\Node\BeforeNodeCopiedEvent

> >     array{
> >       "user": array {"uid": string, "displayName": string},
> >       "time": int,
> >       "event": array{
> >         "class": string,
> >         "source": array{"id": string, "path": string}
> >         "target": array{"id": string, "path": string}
> >       }
> >     }

> - OCP\Files\Events\Node\BeforeNodeRestoredEvent

> >     array{
> >       "user": array {"uid": string, "displayName": string},
> >       "time": int,
> >       "event": array{
> >         "class": string,
> >         "source": array{"id": string, "path": string}
> >         "target": array{"id": string, "path": string}
> >       }
> >     }

> - OCP\Files\Events\Node\BeforeNodeRenamedEvent

> >     array{
> >       "user": array {"uid": string, "displayName": string},
> >       "time": int,
> >       "event": array{
> >         "class": string,
> >         "source": array{"id": string, "path": string}
> >         "target": array{"id": string, "path": string}
> >       }
> >     }

> - OCP\SystemTag\TagAssignedEvent

> >     array {
> >       "user": array {"uid": string, "displayName": string},
> >       "time": int,
> >       "event": array{
> >         "class": string,
> >         "objectType": string (e.g. 'files'),
> >         "objectIds": string[],
> >         "tagIds": int[],
> >       }
> >     }

> - OCP\SystemTag\TagUnassignedEvent

> >     array {
> >       "user": array {"uid": string, "displayName": string},
> >       "time": int,
> >       "event": array{
> >         "class": string,
> >         "objectType": string (e.g. 'files'),
> >         "objectIds": string[],
> >         "tagIds": int[],
> >       }
> >     }

☁️ latest

☁️ Versions
  latest
  stable
  32
  31