Skip to content

List workspace workflow activities ​

GET/api/v2/workspaces/{slug}/workflows/{workflow_id}/activities/

Return the change history of a catalog workflow as a paginated list, oldest first. Each entry records one change: who made it, what changed, and the values before and after.

Plane writes entries in the background after each change, so an entry can appear a moment after the write that caused it.

Path Parameters ​

slug:requiredstring

The workspace slug. It appears in your Plane URLs. In https://app.plane.so/my-team/projects/, the slug is my-team.

workflow_id:requiredstring (uuid)

The catalog workflow whose history you want.

Query Parameters ​

Filters combine with AND.

created_at__gt:optionalstring (date-time)

Return only entries created after this ISO 8601 timestamp. Pass the created_at of the newest entry you have processed to read only what is new since your last poll.

verb:optionalstring

Return only entries with this verb, for example added or updated. Plane doesn't validate the verb, so an unknown verb returns an empty list.

field:optionalstring

Return only entries about this field, for example state_transition or state_approvers. See the field values. Plane doesn't validate the value.

actor_id:optionalstring (uuid)

Return only changes made by this user.

Ordering ​

order_by:optionalstring

Field to sort by. Prefix with - for descending.

  • created_at, -created_at: when each entry was recorded. created_at is the default, oldest first.
  • id, -id

Both are cursor-eligible, so paginate=cursor works with the default ordering.

Pagination ​

per_page:optionalinteger

Page size. Defaults to 50, maximum 200.

offset:optionalinteger

Number 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:optionalstring

Set 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:optionalboolean

Defaults to true. Set to false to skip the COUNT(*) behind total_count. The field is then omitted from the response.

Response shaping ​

fields:optionalstring

Comma-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: actor_id, created_at, epoch, field, id, new_identifier, new_value, old_identifier, old_value, state_id, verb, workflow_id.

See Sparse fields.

Scopes ​

workspaces.workflows:read

Errors ​

StatusCodeCause
400invalid_requestAn unknown name in fields, or a malformed created_at__gt or actor_id.
401unauthorizedMissing or invalid credentials.
402payment_requiredWorkflows isn't on your plan, or is switched off.
403forbiddenYour role or token scope can't view workspace workflows.
404not_foundNo such workflow in this workspace's catalog, or it's outside your tenant.
406not_acceptableThe Accept header asks for a representation the API can't produce.
409states_managed_at_projectThe workspace doesn't have governance turned on. Use the project's workflows instead.
429rate_limitedThrottled. Honor the Retry-After header before retrying.
List workspace workflow activities
bash
curl -X GET \
  "https://api.plane.so/api/v2/workspaces/my-team/workflows/9c1f0b3d-6d2e-4c9a-8b41-2f7e5a0d6c88/activities/?created_at__gt=2026-01-14T09:00:00Z" \
  -H "X-Api-Key: $PLANE_API_KEY"
Response200
json
{
  "data": [
    {
      "id": "1f7a3c9e-6b2d-4e84-a0c5-9d3e7b1f4a28",
      "workflow_id": "9c1f0b3d-6d2e-4c9a-8b41-2f7e5a0d6c88",
      "actor_id": "16c61a3a-512a-48ac-b0be-b6b46fe6f430",
      "state_id": null,
      "verb": "created",
      "field": "workflow",
      "old_value": null,
      "new_value": null,
      "old_identifier": null,
      "new_identifier": "9c1f0b3d-6d2e-4c9a-8b41-2f7e5a0d6c88",
      "epoch": 1768382561.0,
      "created_at": "2026-01-14T09:22:41.478363Z"
    },
    {
      "id": "7c4e9b2a-5f1d-4a36-8e70-2b9d6c3f1a85",
      "workflow_id": "9c1f0b3d-6d2e-4c9a-8b41-2f7e5a0d6c88",
      "actor_id": "16c61a3a-512a-48ac-b0be-b6b46fe6f430",
      "state_id": null,
      "verb": "added",
      "field": "workflow_state",
      "old_value": null,
      "new_value": "In Review",
      "old_identifier": null,
      "new_identifier": "b7e42a19-3c5d-4f80-9a26-8d1c0f4e7b53",
      "epoch": 1768383067.0,
      "created_at": "2026-01-14T09:31:07.215904Z"
    },
    {
      "id": "3a8d1f6c-9e2b-4c57-b4a1-6f0e8d2c9b73",
      "workflow_id": "9c1f0b3d-6d2e-4c9a-8b41-2f7e5a0d6c88",
      "actor_id": "16c61a3a-512a-48ac-b0be-b6b46fe6f430",
      "state_id": "b7e42a19-3c5d-4f80-9a26-8d1c0f4e7b53",
      "verb": "added",
      "field": "state_approvers",
      "old_value": "",
      "new_value": "priya",
      "old_identifier": null,
      "new_identifier": "a3f92c71-6e4b-4d08-9b5a-1c7e3d9f2b84",
      "epoch": 1768475092.0,
      "created_at": "2026-01-15T11:04:52.660219Z"
    }
  ],
  "next": null,
  "previous": null,
  "total_count": 3,
  "pagination": {
    "style": "offset"
  }
}

Poll for new entries ​

To follow a workflow's history without reading it all again:

  1. Read the feed once with the default order_by=created_at, and keep the created_at of the last entry.
  2. On the next poll, pass that value as created_at__gt. Only newer entries come back.
  3. Page through with next, or with next_cursor if you use paginate=cursor, and keep the newest created_at for the following poll.