Comments are the discussion thread on a work item. Create a comment, or update the existing one that carries the same (external_source, external_id) pair.
Both external_source and external_id are required. Without them there is nothing to match on, and the request returns 400.
A created record returns 201 with X-Plane-Upsert: created, and an updated one returns 200 with X-Plane-Upsert: updated. Branch on the header instead of guessing from the status.
Upsert is safe for sequential importers. Two simultaneous upserts of the same key can each miss and each create, so serialize your writes per key.
Visibility of the comment. One of INTERNAL, EXTERNAL.
parent_id:optionalstring (uuid)
Make the comment a reply to this comment. The parent must be a top-level comment on the same work item, and a reply takes its parent's access. Send null for a top-level comment.
Nullable.
external_id:optionalstring
Your system's identifier for this record, for sync and import correlation.
Maximum 255 characters. Nullable.
external_source:optionalstring
The system external_id came from, for example github or jira.
Comma-separated list of fields to return. Fields you don't request are left out of the response instead of coming back as null. id is always included. Pass all to get every requestable field. An unknown field name returns 400. See Sparse fields.
Expanded objects get their own key: ?expand=state keeps state_id and adds a state object next to it. ?fields= and ?expand= work independently. Naming a relation in ?fields= returns 400, and field filtering never removes an expanded object. See Expanding relations.
Upsert a comment
Comments are the discussion thread on a work item. Create a comment, or update the existing one that carries the same
(external_source, external_id)pair.external_sourceandexternal_idare required. Without them there is nothing to match on, and the request returns400.201withX-Plane-Upsert: created, and an updated one returns200withX-Plane-Upsert: updated. Branch on the header instead of guessing from the status.Path parameters
slug:requiredstringThe workspace slug. It appears in your Plane URLs. In
https://app.plane.so/my-team/projects/, the slug ismy-team.project_id:requiredstringThe project the resource belongs to. Accepts the project UUID or its bare identifier, for example
ENG.work_item_id:requiredstringThe work item the resource belongs to. Accepts the work item UUID or its
PROJ-123identifier.Body parameters
comment_html:requiredstringThe comment body, as HTML.
access:optionalstringVisibility of the comment. One of
INTERNAL,EXTERNAL.parent_id:optionalstring (uuid)Make the comment a reply to this comment. The parent must be a top-level comment on the same work item, and a reply takes its parent's
access. Sendnullfor a top-level comment.Nullable.
external_id:optionalstringYour system's identifier for this record, for sync and import correlation.
Maximum 255 characters. Nullable.
external_source:optionalstringThe system
external_idcame from, for examplegithuborjira.Maximum 255 characters. Nullable.
Response shaping
fields:optionalstringComma-separated list of fields to return. Fields you don't request are left out of the response instead of coming back as
null.idis always included. Passallto get every requestable field. An unknown field name returns400. See Sparse fields.Requestable here:
access,actor_id,comment_html,comment_stripped,created_at,created_by_id,edited_at,external_id,external_source,hidden_at,hidden_by_id,hidden_reason,id,parent_id,updated_at,updated_by_id,work_item_id.expand:optionalstringComma-separated relations to embed:
actor.Expanded objects get their own key:
?expand=statekeepsstate_idand adds astateobject next to it.?fields=and?expand=work independently. Naming a relation in?fields=returns400, and field filtering never removes an expanded object. See Expanding relations.Scopes
projects.work_items.comments:writeErrors
400invalid_request401unauthorized402payment_required403forbidden404not_found406not_acceptableAcceptheader asks for a representation the API can't produce.409conflict413payload_too_large415unsupported_media_typeContent-Typeisn't one this endpoint accepts.429rate_limitedRetry-Afterheader before retrying.