List notifications
Return your own notifications in the workspace as a paginated list, newest first. Each request sees only the caller's inbox.
The list covers every kind of record, not only work items. Pass ?entity_name=issue for work item notifications only. Archived notifications are left out unless you pass ?archived=.
Path Parameters
slug:requiredstringThe workspace slug. It appears in your Plane URLs. In https://app.plane.so/my-team/projects/, the slug is my-team.
Query Parameters
Filters combine with AND. A malformed value, such as an unknown trigger_type or an id that isn't a UUID, returns 400 invalid_request instead of an unfiltered page.
read:optionalbooleantrue for notifications you've read, false for unread ones. Omit it for both.
archived:optionalbooleantrue for archived notifications only, false for active ones only. When you omit it, archived notifications are left out.
snoozed:optionalbooleantrue for notifications with a snoozed_till time, false for ones without.
is_mention:optionalbooleantrue for @-mentions of you only, false to leave mentions out.
trigger_type:optionalstringFilter by what triggered the notification. One of mention, assigned, created, or subscribed, the same values as v1's type filter. An unknown value returns 400.
entity_name:optionalstringReturn notifications about one kind of record, for example issue for work items, page, or initiative. Case-insensitive.
Use entity_name__in to match several kinds, comma-separated, for example ?entity_name__in=issue,intake. That variant is case-sensitive.
entity_id:optionalstring (uuid)Return notifications about one record, for example every notification about one work item.
project_id:optionalstring (uuid)Return notifications from one project.
project_identifier:optionalstringReturn notifications from the project with this identifier, for example ENG. Case-insensitive.
triggered_by_id:optionalstring (uuid)Return notifications caused by one user's actions.
created_at__gte:optionalstring (date-time)Return notifications created at or after this time. Pair it with created_at__lte for a window.
created_at__lte:optionalstring (date-time)Return notifications created at or before this time.
search:optionalstringA search term matched against the title and the plain-text message.
Ordering
order_by:optionalstringField to sort by. Prefix with - for descending. Defaults to -created_at.
created_at,-created_atupdated_at,-updated_atread_at,-read_atid,-id
Only created_at and id work with paginate=cursor. Another ordering with a cursor returns 400 ordering_not_cursor_eligible.
Pagination
per_page:optionalintegerPage size. Defaults to 50, maximum 200.
offset:optionalintegerNumber of rows to skip from the start of the result set. Maximum 10000. Read the next value from the response rather than computing offsets yourself.
paginate:optionalstringSet to cursor to opt into the COUNT-free keyset envelope, which returns next_cursor and has_more instead of next and total_count. Omit it for the default offset envelope.
count:optionalbooleanDefaults to true. Set to false to skip the COUNT(*) behind total_count. The field is then omitted from the response.
Response shaping
fields:optionalstringComma-separated list of fields to return on each row. Unrequested keys are omitted from the response, not returned as null, so absent means "not requested" and null means "actually null". id always comes back whether or not you name it.
Pass all for every requestable field. An unknown name is a 400 that lists the valid set and suggests the closest match, so a typo can't silently cost you the saving.
Requestable here: archived_at, created_at, data, entity_id, entity_name, id, is_mention, message, message_html, project_id, read_at, sender, snoozed_till, title, triggered_by_id, updated_at.
See Sparse fields.
expand:optionalstringComma-separated relations to embed alongside the ids: triggered_by (the user whose action produced the notification, with id, display_name, avatar_url, and email).
Expansion is separate-key. ?expand=triggered_by keeps triggered_by_id and adds a triggered_by object next to it, so an id is never replaced by an object. An unknown value is a 400. An expanded object survives field filtering, so ?fields=id,title&expand=triggered_by returns id, title, and triggered_by. See Expanding relations.
Scopes
notifications:read
Errors
| Status | Code | Cause |
|---|---|---|
400 | invalid_request | A filter value is malformed, such as an unknown trigger_type, or ?fields= or ?expand= names something unknown. |
401 | unauthorized | Missing or invalid credentials. |
402 | payment_required | The feature this endpoint belongs to isn't enabled on your plan, or is switched off. |
403 | forbidden | You aren't a member of the workspace, or your token scope doesn't allow this. |
404 | not_found | No such workspace. |
406 | not_acceptable | The Accept header asks for a representation the API can't produce. |
429 | rate_limited | Throttled. Honor the Retry-After header before retrying. |
curl -X GET \
"https://api.plane.so/api/v2/workspaces/my-team/notifications/?read=false&entity_name=issue&created_at__gte=2026-01-14T09:00:00Z" \
-H "X-Api-Key: $PLANE_API_KEY"import requests
response = requests.get(
"https://api.plane.so/api/v2/workspaces/my-team/notifications/",
headers={"X-Api-Key": "your-api-key"},
params={
"read": "false",
"entity_name": "issue",
"created_at__gte": "2026-01-14T09:00:00Z",
},
)
print(response.json())const params = new URLSearchParams({
read: "false",
entity_name: "issue",
created_at__gte: "2026-01-14T09:00:00Z",
});
const response = await fetch(
`https://api.plane.so/api/v2/workspaces/my-team/notifications/?${params}`,
{
headers: {
"X-Api-Key": "your-api-key",
},
},
);
const data = await response.json();{
"data": [
{
"id": "71f4c2a9-8e3b-4d56-a017-5c9e2b8d4f63",
"entity_id": "8c3d1f72-4a9e-4b65-9d20-6e1f3b7a5c48",
"entity_name": "issue",
"project_id": "4af68566-94a4-4eb3-94aa-50dc9427067b",
"title": "Priya Shah has mentioned you in a comment in issue Fix login redirect loop",
"message": null,
"message_html": "<p></p>",
"sender": "in_app:issue_activities:mentioned",
"is_mention": true,
"read_at": null,
"archived_at": null,
"snoozed_till": null,
"data": {
"issue": {
"id": "8c3d1f72-4a9e-4b65-9d20-6e1f3b7a5c48",
"name": "Fix login redirect loop",
"identifier": "ENG",
"sequence_id": 142,
"state_name": "In Progress",
"state_group": "started",
"type_id": "5b7e2d94-1a6c-4f38-9e02-8d4c3b6a1f57"
},
"issue_activity": {
"id": "3f8b6d21-9c4e-4a73-b1d5-7e2a0c9f6b48",
"verb": "created",
"field": "comment",
"actor": "16c61a3a-512a-48ac-b0be-b6b46fe6f430",
"new_value": "@Ravi can you check the redirect on Safari?",
"old_value": "None",
"old_identifier": null,
"new_identifier": null,
"issue_comment": "@Ravi can you check the redirect on Safari?"
}
},
"triggered_by_id": "16c61a3a-512a-48ac-b0be-b6b46fe6f430",
"created_at": "2026-01-14T09:22:41.478363Z",
"updated_at": "2026-01-14T09:22:41.478363Z"
},
{
"id": "2a8e6c14-7d93-4f0b-b5e2-1c4a9f7d3e86",
"entity_id": "8c3d1f72-4a9e-4b65-9d20-6e1f3b7a5c48",
"entity_name": "issue",
"project_id": "4af68566-94a4-4eb3-94aa-50dc9427067b",
"title": "updated the priority to",
"message": null,
"message_html": "<p></p>",
"sender": "in_app:issue_activities:assigned",
"is_mention": false,
"read_at": null,
"archived_at": null,
"snoozed_till": null,
"data": {
"issue": {
"id": "8c3d1f72-4a9e-4b65-9d20-6e1f3b7a5c48",
"name": "Fix login redirect loop",
"identifier": "ENG",
"sequence_id": 142,
"state_name": "In Progress",
"state_group": "started",
"type_id": "5b7e2d94-1a6c-4f38-9e02-8d4c3b6a1f57"
},
"issue_activity": {
"id": "c09a4e7b-2d61-4f85-93b8-6a1e5d2c7f40",
"verb": "updated",
"field": "priority",
"actor": "16c61a3a-512a-48ac-b0be-b6b46fe6f430",
"new_value": "urgent",
"old_value": "medium",
"old_identifier": null,
"new_identifier": null
}
},
"triggered_by_id": "16c61a3a-512a-48ac-b0be-b6b46fe6f430",
"created_at": "2026-01-14T09:18:05.112874Z",
"updated_at": "2026-01-14T09:18:05.112874Z"
}
],
"next": null,
"previous": null,
"total_count": 2,
"pagination": {
"style": "offset"
}
}{
"type": "invalid_request",
"code": "invalid_request",
"detail": "One or more fields failed validation.",
"errors": [
{
"field": "trigger_type",
"code": "invalid_choice",
"message": "Select a valid choice. telepathy is not one of the available choices."
}
]
}
