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 is a 400.
A created record answers 201 with X-Plane-Upsert: created; an updated one answers 200 with X-Plane-Upsert: updated. Branch on the header rather than 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.
Comma-separated list of fields to return. Unrequested keys are omitted, not returned as null. id always comes back. Pass all for every requestable field. An unknown name is a 400. See Sparse fields.
Expansion is separate-key — ?expand=state keeps state_id and adds a state object next to it. ?fields= and ?expand= are independent: naming a relation in ?fields= is a 400, and expanded objects survive field filtering. 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 is a400.201withX-Plane-Upsert: created; an updated one answers200withX-Plane-Upsert: updated. Branch on the header rather than 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 hangs off. Accepts the work item UUID or its
PROJ-123identifier.Body Parameters
comment_html:requiredstringThe comment html.
access:optionalstringINTERNAL- INTERNALEXTERNAL- EXTERNALOne of
INTERNAL,EXTERNAL.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. Unrequested keys are omitted, not returned as
null.idalways comes back. Passallfor every requestable field. An unknown name is a400. See Sparse fields.Requestable here:
access,actor_id,comment_html,comment_stripped,created_at,created_by_id,edited_at,external_id,external_source,id,work_item_id.expand:optionalstringComma-separated relations to embed:
actor.Expansion is separate-key —
?expand=statekeepsstate_idand adds astateobject next to it.?fields=and?expand=are independent: naming a relation in?fields=is a400, and expanded objects survive field filtering. 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.