diff --git a/admin_manual/webhook_listeners/index.rst b/admin_manual/webhook_listeners/index.rst index a9a43d1649c..530f71ed142 100644 --- a/admin_manual/webhook_listeners/index.rst +++ b/admin_manual/webhook_listeners/index.rst @@ -7,7 +7,7 @@ Webhook Listeners Introduction ------------ -Nextcloud supports sending notifications to external services whenever something +Nextcloud supports sending notifications to external services whenever something important happens, such as when files are changed or updated. Overview @@ -20,13 +20,6 @@ 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 @@ -46,7 +39,7 @@ Enable the ``webhook_listeners`` app that comes bundled with Nextcloud - e.g. Listening to events ------------------- -You can use the OCS API to add webhooks for specific events. See: +You can use the OCS API to add webhooks for specific events. See: `Register a new webhook `_. .. TODO ON RELEASE: Update version number above upon release. @@ -54,13 +47,46 @@ You can use the OCS API to add webhooks for specific events. See: Note: When authenticating with the OCS API to register webhooks, the account you use must have administrator rights or delegated administrator rights. +Listing registered webhooks +~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +You can list all currently registered webhook listeners from the command line: + +.. code-block:: bash + + occ webhook_listeners:list + +By default this prints a table. Use the ``--output`` option to change the format. +Each row contains the full configuration of a registered webhook + 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. +When registering a webhook listener, you can specify a filter parameter (``eventFilter``). +The filter is evaluated against the **complete webhook payload envelope**, which has this +structure: + +.. code-block:: json + + { + "event": { "class": "…", "…event-specific fields…" }, + "user": { "uid": "…", "displayName": "…" }, + "time": 1700100000 + } + +Filter keys use **dot-notation** to traverse into nested objects. For example: + +- ``time`` — matches the top-level Unix timestamp +- ``user.uid`` — matches the ``uid`` field inside the ``user`` object +- ``event.class`` — matches the event's fully-qualified class name inside ``event`` +- ``event.node.path`` — matches the ``path`` field inside ``event`` → ``node`` + (for node events) +- ``event.calendarId`` — matches the ``calendarId`` field inside ``event`` + (for calendar events) + +The value of the filter 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``. @@ -76,12 +102,20 @@ inside the ``Special folder`` of any user. Note that the slashes in the path nee 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 } }}`` +You can also use comparison operators (``$e``, ``$ne``, ``$gt``, ``$gte``, +``$lt``, ``$lte``, ``$in``, ``$nin``, ``$all``, ``$exists``, ``$mod``) 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. +Use ``{ "event.node.id": { "$exists": true } }`` to match only events where the node +still has an ID (i.e. is not a ``NonExistingFile``/``NonExistingFolder``). + +Use ``{ "event.class": "OCP\\Files\\Events\\Node\\NodeCreatedEvent" }`` to match only +``NodeCreatedEvent`` events (backslashes must be escaped in JSON strings). + Speeding up webhook dispatch ~~~~~~~~~~~~~~~~~~~~~~~~~~~~ @@ -94,9 +128,9 @@ 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 +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 +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. .. code-block:: @@ -172,571 +206,378 @@ It is recommended to restart this worker at least once a day to make sure code c 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 - - .. code-block:: text - - 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, - }, - } - } +This is list of typically available events. It features the event ID and the available variables for filtering. - * OCA\\Tables\\Event\\RowAddedEvent +.. note:: - .. code-block:: text + In addition to the events listed below (which are provided by Nextcloud server and included apps), optional apps may register their own webhook-compatible events. The exact set of events available on your server will depend on your Nextcloud version and installed apps. If you need to discover available events, consult your app documentation or refer to your app source code or the Nextcloud developer documentation. - array{ - "user": array {"uid": string, "displayName": string}, - "time": int, - "event": array{ - "class": string, - "tableId": int, - "rowId": int, - "previousValues": null|array, - "values": null|array - } - } +Payload envelope +~~~~~~~~~~~~~~~~ - * OCA\\Tables\\Event\\RowDeletedEvent +Every webhook HTTP POST body shares the same top-level structure: - .. code-block:: text +.. code-block:: json - array{ - "user": array {"uid": string, "displayName": string}, - "time": int, - "event": array{ - "class": string, - "tableId": int, - "rowId": int, - "previousValues": null|array, - "values": null|array - } - } - - * OCA\\Tables\\Event\\RowUpdatedEvent - - .. code-block:: text - - array{ - "user": array {"uid": string, "displayName": string}, - "time": int, - "event": array{ - "class": string, - "tableId": int, - "rowId": int, - "previousValues": null|array, - "values": null|array - } - } + { + "event": {}, + "user": { "uid": "alice", "displayName": "Alice" }, + "time": 1700100000 + } - * OCP\\Calendar\\Events\\CalendarObjectCreatedEvent - - .. code-block:: text - - 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, - "objectData": array{ - "id": int, - "uri": string, - "lastmodified": int, - "etag": string, - "calendarid": int, - "size": int, - "component": string|null, - "classification": int - } - } - } +Where: - * OCP\\Calendar\\Events\\CalendarObjectDeletedEvent - - .. code-block:: text - - 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, - "objectData": array{ - "id": int, - "uri": string, - "lastmodified": int, - "etag": string, - "calendarid": int, - "size": int, - "component": string|null, - "classification": int - } - } - } +- ``"event"`` is an object containing the event-specific data **plus** a ``"class"`` field + holding the fully-qualified PHP class name (e.g. + ``"OCP\\Files\\Events\\Node\\NodeCreatedEvent"``). +- ``"user"`` is the user who triggered the event (``null`` when no user session exists). + Contains ``"uid"`` (string) and ``"displayName"`` (string). +- ``"time"`` is an integer Unix timestamp of when the event was captured. - * OCP\\Calendar\\Events\\CalendarObjectMovedEvent - - .. code-block:: text - - 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, - "targetShares": list, - "objectData": array{ - "id": int, - "uri": string, - "lastmodified": int, - "etag": string, - "calendarid": int, - "size": int, - "component": string|null, - "classification": int - } - } - } +**Note:** Example payloads below are based on actual webhook HTTP POST payloads, using +real JSON and data types. Field types are indicated by their value type: for example, +``"id": 123`` is an integer, not a string. - * OCP\\Calendar\\Events\\CalendarObjectMovedToTrashEvent - - .. code-block:: text - - 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, - "objectData": array{ - "id": int, - "uri": string, - "lastmodified": int, - "etag": string, - "calendarid": int, - "size": int, - "component": string|null, - "classification": int - } - } - } +Node (Files / Folders) Events +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - * OCP\\Calendar\\Events\\CalendarObjectRestoredEvent - - .. code-block:: text - - 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, - "objectData": array{ - "id": int, - "uri": string, - "lastmodified": int, - "etag": string, - "calendarid": int, - "size": int, - "component": string|null, - "classification": int - } - } - } +Node events include those that act on a single node as well as those that act on two +nodes (such as copy, rename, and restore). In single-node events, the ``event`` object +includes a ``node`` object. In two-node events, it includes both a ``source`` and a +``target`` object. The examples below show the **complete** payloads. - * OCP\\Calendar\\Events\\CalendarObjectUpdatedEvent - - .. code-block:: text - - 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, - "objectData": array{ - "id": int, - "uri": string, - "lastmodified": int, - "etag": string, - "calendarid": int, - "size": int, - "component": string|null, - "classification": int - } - } - } +**1. Single-node events** - * OCP\\Files\\Events\\Node\\BeforeNodeCreatedEvent +Applies to: - .. code-block:: text +- ``OCP\Files\Events\Node\BeforeNodeCreatedEvent`` +- ``OCP\Files\Events\Node\BeforeNodeTouchedEvent`` +- ``OCP\Files\Events\Node\BeforeNodeWrittenEvent`` +- ``OCP\Files\Events\Node\BeforeNodeReadEvent`` +- ``OCP\Files\Events\Node\BeforeNodeDeletedEvent`` +- ``OCP\Files\Events\Node\NodeCreatedEvent`` +- ``OCP\Files\Events\Node\NodeTouchedEvent`` +- ``OCP\Files\Events\Node\NodeWrittenEvent`` +- ``OCP\Files\Events\Node\NodeDeletedEvent`` - array{ - "user": array {"uid": string, "displayName": string}, - "time": int, - "event": array{ - "class": string, - "node": array{"id": string, "path": string} - } - } +.. code-block:: json - * OCP\\Files\\Events\\Node\\BeforeNodeTouchedEvent - - .. code-block:: text - - array{ - "user": array {"uid": string, "displayName": string}, - "time": int, - "event": array{ - "class": string, - "node": array{"id": string, "path": string} - } - } - - * OCP\\Files\\Events\\Node\\BeforeNodeWrittenEvent - - .. code-block:: text - - array{ - "user": array {"uid": string, "displayName": string}, - "time": int, - "event": array{ - "class": string, - "node": array{"id": string, "path": string} - } - } - - * OCP\\Files\\Events\\Node\\BeforeNodeReadEvent - - .. code-block:: text - - array{ - "user": array {"uid": string, "displayName": string}, - "time": int, - "event": array{ - "class": string, - "node": array{"id": string, "path": string} - } - } - - * OCP\\Files\\Events\\Node\\BeforeNodeDeletedEvent - - .. code-block:: text - - array{ - "user": array {"uid": string, "displayName": string}, - "time": int, - "event": array{ - "class": string, - "node": array{"id": string, "path": string} - } - } - - * OCP\\Files\\Events\\Node\\NodeCreatedEvent - - .. code-block:: text - - array{ - "user": array {"uid": string, "displayName": string}, - "time": int, - "event": array{ - "class": string, - "node": array{"id": string, "path": string} - } + { + "event": { + "class": "OCP\\Files\\Events\\Node\\NodeCreatedEvent", + "node": { + "id": 437, + "path": "/admin/files/test-webhook.txt" + } + }, + "user": { + "uid": "admin", + "displayName": "Admin" + }, + "time": 1700100000 + } + +Where: + +- ``"event.class"`` is the fully-qualified event class name (string) +- ``"event.node.id"`` is an integer (unique node ID) — may be absent, see note below +- ``"event.node.path"`` is a string (file/folder path) + +.. note:: + + In some events (such as ``NodeDeletedEvent`` or when the node refers to a + non-existent file or folder), the ``node`` object may not have an ``id`` field. + This happens when the node no longer exists in the filesystem/database, and is + represented in Nextcloud by an internal ``NonExistingFile`` or ``NonExistingFolder``. + In such cases, only the ``path`` field will be present. Always check for the + presence of the ``id`` field in these payloads. Usually, the corresponding ``Before*`` + event (such as ``BeforeNodeDeletedEvent``) can be used if you require the ``id`` of + the node before deletion. + +Example of a deleted node webhook payload: + +.. code-block:: json + + { + "event": { + "class": "OCP\\Files\\Events\\Node\\NodeDeletedEvent", + "node": { + "path": "/user/files/oldfile.txt" + } + }, + "user": { + "uid": "user", + "displayName": "User" + }, + "time": 1700100500 + } + +**2. Two-node events** + +Applies to: + +- ``OCP\Files\Events\Node\NodeCopiedEvent`` +- ``OCP\Files\Events\Node\NodeRenamedEvent`` +- ``OCP\Files\Events\Node\NodeRestoredEvent`` +- ``OCP\Files\Events\Node\BeforeNodeCopiedEvent`` +- ``OCP\Files\Events\Node\BeforeNodeRestoredEvent`` +- ``OCP\Files\Events\Node\BeforeNodeRenamedEvent`` + +.. code-block:: json + + { + "event": { + "class": "OCP\\Files\\Events\\Node\\NodeRestoredEvent", + "source": { + "id": 399, + "path": "/admin/files_trashbin/files/myfile.txt" + }, + "target": { + "id": 437, + "path": "/admin/files/myfile.txt" + } + }, + "user": { + "uid": "admin", + "displayName": "Admin" + }, + "time": 1700100000 } - * OCP\\Files\\Events\\Node\\NodeTouchedEvent +Where: - .. code-block:: text +- ``"source"`` is an object representing the original node, with ``"id"`` (integer) and ``"path"`` (string) +- ``"target"`` is an object representing the resulting/copied/restored/renamed node, with ``"id"`` (integer) and ``"path"`` (string) - array{ - "user": array {"uid": string, "displayName": string}, - "time": int, - "event": array{ - "class": string, - "node": array{"id": string, "path": string} - } - } +.. note:: - * OCP\\Files\\Events\\Node\\NodeWrittenEvent + For some two-node events, the ``source`` or ``target`` node may not have an ``id`` field, for instance if the node was deleted or is otherwise missing from the filesystem. Always check for the presence of the ``id`` field in these objects. Typically, if you need the ``id`` of a node just before deletion or change, the respective ``Before*`` event (such as ``BeforeNodeRenamedEvent``) will include it. - .. code-block:: text +Example of a two-node event (NodeRenamedEvent) where the source node is missing: - array{ - "user": array {"uid": string, "displayName": string}, - "time": int, - "event": array{ - "class": string, - "node": array{"id": string, "path": string} - } - } +.. code-block:: json - * OCP\\Files\\Events\\Node\\NodeDeletedEvent - - .. code-block:: text - - array{ - "user": array {"uid": string, "displayName": string}, - "time": int, - "event": array{ - "class": string, - "node": array{"id": string, "path": string} - } + { + "event": { + "class": "OCP\\Files\\Events\\Node\\NodeRenamedEvent", + "source": { + "path": "/user/files/previousname.txt" + }, + "target": { + "id": 599, + "path": "/user/files/newname.txt" + } + }, + "user": { + "uid": "user", + "displayName": "Joe User" + }, + "time": 1700100000 } - * OCP\\Files\\Events\\Node\\NodeCopiedEvent +.. note:: - .. code-block:: text + Only these fields are guaranteed by Nextcloud core. Additional fields may appear if added by custom plugins or future versions. - 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} - } - } +System Tag Events +~~~~~~~~~~~~~~~~~ - * OCP\\Files\\Events\\Node\\NodeRestoredEvent +* ``OCP\SystemTag\TagAssignedEvent`` +* ``OCP\SystemTag\TagUnassignedEvent`` - .. code-block:: text +.. code-block:: json - 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} - } + { + "event": { + "class": "OCP\\SystemTag\\TagAssignedEvent", + "objectType": "files", + "objectIds": ["437", "438"], + "tagIds": [3, 17] + }, + "user": { + "uid": "admin", + "displayName": "Admin" + }, + "time": 1700100000 } - * OCP\\Files\\Events\\Node\\NodeRenamedEvent - - .. code-block:: text - - 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} - } - } +Calendar Object Events +~~~~~~~~~~~~~~~~~~~~~~ - * OCP\\Files\\Events\\Node\\BeforeNodeCopiedEvent +Calendar object events use two distinct payload formats, depending on the event. - .. code-block:: text +**Standard payload** - 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} - } - } +Applies to: - * OCP\\Files\\Events\\Node\\BeforeNodeRestoredEvent +- ``OCP\Calendar\Events\CalendarObjectCreatedEvent`` +- ``OCP\Calendar\Events\CalendarObjectDeletedEvent`` +- ``OCP\Calendar\Events\CalendarObjectMovedToTrashEvent`` +- ``OCP\Calendar\Events\CalendarObjectRestoredEvent`` +- ``OCP\Calendar\Events\CalendarObjectUpdatedEvent`` - .. code-block:: text +.. code-block:: json - 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} - } + { + "event": { + "class": "OCP\\Calendar\\Events\\CalendarObjectCreatedEvent", + "calendarId": 9, + "calendarData": { + "id": 9, + "uri": "work", + "{http://calendarserver.org/ns/}getctag": "1736283", + "{http://sabredav.org/ns}sync-token": 9651, + "{urn:ietf:params:xml:ns:caldav}supported-calendar-component-set": "VEVENT,VTODO", + "{urn:ietf:params:xml:ns:caldav}schedule-calendar-transp": "opaque" + }, + "shares": [ + { + "href": "mailto:alice@example.com", + "commonName": "Alice", + "status": 2, + "readOnly": false, + "{http://owncloud.org/ns}principal": "principal:users/alice", + "{http://owncloud.org/ns}group-share": false + } + ], + "objectData": { + "id": 22, + "uri": "event-20251111T100000Z.ics", + "lastmodified": 1700099500, + "etag": "19fa45b394", + "calendarid": 9, + "size": 4096, + "component": "VEVENT", + "classification": 0 + } + }, + "user": { + "uid": "david", + "displayName": "David" + }, + "time": 1700100000 } - * OCP\\Files\\Events\\Node\\BeforeNodeRenamedEvent - - .. code-block:: text +**Distinct payload for two-calendar events** - 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} - } - } +Applies to: - * OCP\\SystemTag\\TagAssignedEvent +- ``OCP\Calendar\Events\CalendarObjectMovedEvent`` - .. code-block:: text +.. code-block:: json - array { - "user": array {"uid": string, "displayName": string}, - "time": int, - "event": array{ - "class": string, - "objectType": string (e.g. 'files'), - "objectIds": string[], - "tagIds": int[], - } + { + "event": { + "class": "OCP\\Calendar\\Events\\CalendarObjectMovedEvent", + "sourceCalendarId": 9, + "sourceCalendarData": { + "id": 9, + "uri": "work", + }, + "targetCalendarId": 11, + "targetCalendarData": { + "id": 11, + "uri": "meetings", + }, + "sourceShares": [ + { + "href": "mailto:alice@example.com", + "commonName": "Alice" + } + ], + "targetShares": [ + { + "href": "mailto:bob@example.com", + "commonName": "Bob" + } + ], + "objectData": { + "id": 22, + "uri": "event-20251111T100000Z.ics" + }, + }, + "user": { + "uid": "david", + "displayName": "David" + }, + "time": 1700100000 } - * OCP\\SystemTag\\TagUnassignedEvent - - .. code-block:: text - - array { - "user": array {"uid": string, "displayName": string}, - "time": int, - "event": array{ - "class": string, - "objectType": string (e.g. 'files'), - "objectIds": string[], - "tagIds": int[], - } - } +Forms App Events +~~~~~~~~~~~~~~~~ + +When the optional ``forms`` app is installed: + +* ``OCA\Forms\Events\FormSubmittedEvent`` + +.. code-block:: json + + { + "event": { + "class": "OCA\\Forms\\Events\\FormSubmittedEvent", + "form": { + "id": 51, + "hash": "abc123def456", + "title": "Employee Feedback", + "description": "Annual employee feedback form.", + "ownerId": "alice", + "fileId": 1002, + "fileFormat": "pdf", + "created": 1700001000, + "access": 0, + "expires": 1702606600, + "isAnonymous": false, + "submitMultiple": false, + "showExpiration": true, + "lastUpdated": 1700001200, + "submissionMessage": null, + "state": 1 + }, + "submission": { + "id": 220, + "formId": 51, + "userId": "bob", + "timestamp": 1700001234 + } + }, + "user": { + "uid": "bob", + "displayName": "Bob" + }, + "time": 1700001234, + } + +Tables App Events +~~~~~~~~~~~~~~~~~ + +When the optional ``tables`` app is installed: + +- ``OCA\Tables\Event\RowAddedEvent`` +- ``OCA\Tables\Event\RowDeletedEvent`` +- ``OCA\Tables\Event\RowUpdatedEvent`` + +.. code-block:: json + + { + "event": { + "class": "OCA\\Tables\\Event\\RowAddedEvent", + "tableId": 34, + "rowId": 7, + "previousValues": null, + "values": { + "0": "Project X", + "1": 2025, + "2": "active" + } + }, + "user": { + "uid": "carol", + "displayName": "Carol" + }, + "time": 1700054321, + } + +.. note:: + + For filtering or automation, always check the actual payload you receive, as it matches + the JSON examples above, not PHPDoc or internal PHP array type style.