Activity API
The Activity app provides an API for other Nextcloud apps to publish events, display them in the activity stream, and let users control their notification preferences. It also exposes a REST API for clients to consume the activity stream.
The extension API consists of four interfaces, all in the OCP
namespace:
IEvent — The activity event object
IProvider — Parses and translates activities for display
ISetting — Defines activity types and notification preferences
IFilter — Adds filters to the activity stream sidebar
All three extension interfaces (IProvider, ISetting, IFilter) are
registered in your app’s appinfo/info.xml.
Registering activity components
Add an <activity> section to your appinfo/info.xml to register
providers, settings, and filters:
<?xml version="1.0"?>
<info>
...
<activity>
<settings>
<setting>OCA\MyApp\Activity\Setting</setting>
</settings>
<filters>
<filter>OCA\MyApp\Activity\Filter</filter>
</filters>
<providers>
<provider>OCA\MyApp\Activity\Provider</provider>
</providers>
</activity>
</info>
Each value is the fully qualified class name of the implementation.
Creating and publishing events
To create and publish an activity event, generate an IEvent from
the activity manager and call publish():
<?php
// IManager is injected via dependency injection
$event = $this->activityManager->generateEvent();
$event->setApp('myapp')
->setType('myapp_action')
->setAffectedUser('targetUser')
->setSubject('subject_key', ['param1' => 'value1'])
->setObject('myobject', 42, 'Object Name');
$this->activityManager->publish($event);
Required fields
The following must be set before publishing:
setApp()— Your app IDsetType()— Must match anISetting::getIdentifier()setAffectedUser()— The user who will see this activitysetSubject()— A subject key and optional parameterssetObject()— The object type, ID, and name
Optional fields
setAuthor()— If unset, the current user is usedsetTimestamp()— If unset, the current time is usedsetMessage()— An additional message key and parameterssetLink()— Should typically be set inIProvider::parse()insteadsetIcon()— Should typically be set inIProvider::parse()instead
Note
Do not call setParsedSubject(), setRichSubject(),
setParsedMessage(), setRichMessage(), or
setChildEvent() when publishing. These are not persisted and
should be set in your provider’s parse() method.
Implementing a provider
Providers implement OCP\Activity\IProvider to parse, translate,
and beautify activities for display. The interface has a single
parse() method.
Check responsibility
First, check whether the event belongs to your app. If not, throw
UnknownActivityException so the activity app passes it to the
next provider:
<?php
public function parse(string $language, IEvent $event, ?IEvent $previousEvent = null): IEvent {
if ($event->getApp() !== 'myapp') {
throw new \OCP\Activity\Exceptions\UnknownActivityException();
}
// ... parse the event
}
Note
UnknownActivityException was added in Nextcloud 30.
For older versions, throw \InvalidArgumentException instead.
Set the parsed subject
At minimum, you must call setParsedSubject() with a translated,
plain-text string:
$event->setParsedSubject(
$this->l->t('You created %1$s', [$event->getObjectName()])
);
Additionally, you should call:
setIcon()— A full URL to an icon for the activitysetRichSubject()— A translated string with placeholders and a parameter array for rich display
Note
Starting with Nextcloud 26, calling setRichSubject()
automatically generates a parsed subject, so a separate
setParsedSubject() call is no longer required.
Rich object strings
Rich subjects allow the UI to render interactive elements like file
links and user avatars. The setRichSubject() method takes a
translated string with placeholders and an array of typed objects:
$event->setRichSubject(
$this->l->t('You added {file} to your favorites'),
['file' => [
'type' => 'file',
'id' => (string) $event->getObjectId(),
'name' => basename($event->getObjectName()),
'path' => $event->getObjectName(),
]]
);
Available object types and their required keys are defined in OCP\RichObjectStrings\Definitions.
Short vs. long translations
File-related activities support a short form (e.g. “Added to
favorites”) for the sidebar and a long form (e.g. “You added
hello.jpg to your favorites”) for the stream. Check
IManager::isFormattingFilteredObject() to decide which form to
use.
Merging activities
Related activities can be merged automatically using
OCP\Activity\IEventMerger (inject via dependency injection).
The merger combines events when all of the following are met:
Same
getApp()No message set (
getMessage()is empty)Same
getSubject()Same
getObjectType()Time difference is less than 3 hours
At most 5 events are merged
Do not merge events manually if these requirements are not met.
Implementing a setting
Settings define activity types and appear on the personal
notification preferences page. They implement
OCP\Activity\ISetting.
Method |
Description |
|---|---|
|
Unique ID (lowercase |
|
Translated, short description. May use |
|
Absolute URL to a 32x32 pixel icon (SVG preferred). |
|
|
|
Whether stream display is enabled by default for new users. |
|
Whether email notifications are enabled by default. |
|
Whether users can toggle stream display. |
|
Whether users can toggle email notifications. |
When both canChangeStream() and canChangeMail() return
false, the setting is hidden from the personal page entirely.
Implementing a filter
Filters restrict the activity stream and appear in the sidebar
navigation. They implement OCP\Activity\IFilter.
Method |
Description |
|---|---|
|
Unique ID (lowercase |
|
Translated label, 1-3 short words. |
|
Absolute URL to a 32x32 pixel icon (SVG preferred). |
|
|
|
Return an array of app IDs to show, or an empty array for all apps. |
|
Receives a list of type identifiers (from |
Example filtering to only show calendar todo activities:
public function filterTypes(array $types): array {
return array_intersect(['calendar_todo'], $types);
}
REST API (v2)
The Activity app exposes a REST API for clients to consume the activity stream.
Capabilities
The API advertises its features via capabilities:
GET /ocs/v2.php/cloud/capabilities
<activity>
<apiv2>
<element>filters</element>
<element>previews</element>
<element>rich-strings</element>
</apiv2>
</activity>
Endpoints
GET /ocs/v2.php/apps/activity/api/v2/activity
GET /ocs/v2.php/apps/activity/api/v2/activity/{filter}
GET /ocs/v2.php/apps/activity/api/v2/activity/filters
Parameters
Name |
Type |
Description |
|---|---|---|
|
int |
ID of the last activity seen (optional) |
|
int |
Number of activities to return (default: |
|
string |
Filter by object type. Requires |
|
string |
Filter by object ID. Requires |
|
string |
|
HTTP status codes
Code |
Description |
|---|---|
|
Activities returned |
|
User has no activities selected for the stream |
|
ETag matches or end of list reached |
|
Offset activity belongs to another user, or not logged in |
|
Unknown filter |
Response headers
LinkURL for the next page of results, including all parameters:
<https://cloud.example.com/ocs/v2.php/apps/activity/api/v2/activity/all?since=364>; rel="next"X-Activity-First-KnownID of the first known activity (when
sincewas not recognized).X-Activity-Last-GivenID to use as
sincefor the next request.
Activity element fields
Field |
Type |
Description |
|---|---|---|
|
int |
Unique activity ID |
|
string |
ISO 8601 timestamp |
|
string |
App that created the activity |
|
string |
Activity type identifier |
|
string |
User ID of the actor (may be empty) |
|
string |
Translated plain-text subject |
|
array |
Rich subject: |
|
string |
Translated plain-text message (optional) |
|
array |
Rich message, same format as |
|
string |
Full URL to the activity icon (optional) |
|
string |
Full URL to the relevant location (optional) |
|
string |
Type of the related object (optional) |
|
int |
ID of the related object (optional) |
|
string |
Name or path of the related object (optional) |
|
array |
List of preview elements for file activities (optional) |
Example response
{
"activity_id": 1,
"datetime": "2015-11-20T12:49:31+00:00",
"app": "files",
"type": "file_created",
"user": "test1",
"subject": "test1 created hello.txt",
"subject_rich": {
"0": "{user1} created {file1}",
"1": {
"user1": {
"type": "user",
"id": "test1",
"name": "Test User"
},
"file1": {
"type": "file",
"id": 23,
"name": "hello.txt",
"path": "/test/hello.txt"
}
}
},
"icon": "https://cloud.example.com/apps/files/img/add-color.svg",
"link": "https://cloud.example.com/apps/files/?dir=/test",
"object_type": "files",
"object_id": 23,
"object_name": "/test/hello.txt",
"previews": [
{
"source": "https://cloud.example.com/core/preview.png?file=/hello.txt&x=150&y=150",
"link": "https://cloud.example.com/apps/files/?dir=/test&scrollto=hello.txt",
"mimeType": "text/plain",
"fileId": 23,
"view": "files",
"isMimeTypeIcon": false,
"filename": "hello.txt"
}
]
}
Note
Additional fields not listed above may appear in responses for backwards compatibility but should be ignored.
Further reading
The Activity app repository contains additional documentation with more detailed examples: