Skip to content

feat(api): expose work item subscribers on the API-key surface - #3

Merged
thestumonkey merged 2 commits into
planeforkfrom
bug-reporter-subscribers
Aug 29, 2026
Merged

feat(api): expose work item subscribers on the API-key surface#3
thestumonkey merged 2 commits into
planeforkfrom
bug-reporter-subscribers

Conversation

@thestumonkey

Copy link
Copy Markdown
Member

Why

Upstream exposes issue subscribers only on the session-authenticated app API (/api/workspaces/.../issues/<id>/issue-subscribers/), where subscribe can act on request.user and nobody else. That is unusable for an integration: an API token authenticates as its own bot user, so there is no way for an external system to say "notify this person about this work item".

We need exactly that. Bug reports filed from the Psyclo apps land in Intake as work items owned by the token user, and a reporter who agreed to be contacted has to receive Plane's own comment notification email for the thread to be two-way.

Everything else that loop needs already exists on /api/v1/ — workspace invitations, project members, work item comments. This was the one gap.

What

GET / POST / DELETE on:

workspaces/<slug>/projects/<project_id>/work-items/<issue_id>/subscribers/

POST accepts either subscriber (a user id) or email, because a calling system generally knows the person by address and would otherwise have to page the whole member list to translate it.

Design notes

Permissions are split by method. Reading who is subscribed is ordinary ProjectEntityPermission. Changing someone else's notification settings is not ordinary entity access, so writes require ProjectAdminPermission.

The project-membership check in POST is enforced, not merely documented. bgtasks/notification_task.py filters recipients to active ProjectMember rows before intersecting with IssueSubscriber, so accepting a non-member would create a row that looks correct in the database and silently never delivers anything. Rejecting it with a clear 400 is better than a subscription that quietly does nothing.

get_or_create, so a repeated call is a 200 rather than a duplicate or an error — the calling integration is written to be re-runnable.

Files

  • plane/api/views/subscriber.py — new
  • plane/api/urls/subscriber.py — new
  • plane/api/urls/__init__.py, plane/api/views/__init__.py — registration only

Follows the ProjectMemberListCreateAPIEndpoint shape: BaseAPIView, slug-scoped, drf_spectacular annotations for the generated OpenAPI schema.

Verification

Syntax checked and every import verified to resolve (IssueSubscriber, ProjectMember, both permission classes, all five openapi symbols).

Not verified: Django has not been started against this, so the endpoint has never actually executed. Worth a smoke test on staging before it is relied on.

Consumer

Ushadow-io/psyclo#398

🤖 Generated with Claude Code

https://claude.ai/code/session_01SsBurBNYE4Wxpx2k6atqcj

thestumonkey and others added 2 commits August 28, 2026 12:46
Upstream exposes issue subscribers only on the session-authenticated app API,
where `subscribe` can act on `request.user` and nobody else. That is unusable
for an integration: an API token authenticates as its own bot user, so there
is no way for an external system to say "notify THIS person about THIS work
item".

We need exactly that. Bug reports filed from the Psyclo apps land in Intake as
work items owned by the token user, and a reporter who agreed to be contacted
has to receive Plane's own comment notification email for the thread to be
two-way. Everything else that loop needs already exists on /api/v1/ --
workspace invitations, project members, work item comments -- this was the one
gap.

Adds GET/POST/DELETE on

  workspaces/<slug>/projects/<project_id>/work-items/<issue_id>/subscribers/

POST takes either `subscriber` (a user id) or `email`, because a calling
system generally knows the person by address and would otherwise have to page
the whole member list to translate it.

Reads are ProjectEntityPermission; changing someone else's notifications is
not ordinary entity access, so writes require ProjectAdminPermission.

The project-membership check in POST is deliberate rather than merely
documented: `bgtasks/notification_task.py` filters recipients to active
ProjectMember rows before intersecting with IssueSubscriber, so accepting a
non-member would create a row that looks correct in the database and silently
never delivers anything. Better to reject it.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01SsBurBNYE4Wxpx2k6atqcj
@github-actions

Copy link
Copy Markdown

React Doctor found no new issues. 🎉

Reviewed by React Doctor for commit 2699a9a.

@thestumonkey
thestumonkey merged commit 66da3f2 into planefork Aug 29, 2026
11 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant