---
title: "The activity log across the person's monorepos (JSON pages, or the person's WebSocket)"
description: "The web home's log: what happened in every monorepo the person belongs\nto, newest first, each line with its `monorepo`. People, machines and\nworktrees only: no agent field.\n\nLines are the activity kinds: `push`, `branch.opened`,\n`branch.deleted`, `merged`, `review`, `status` (only when a check's\nstate changes), `worktree.reported` (a new worktree only),\n`worktree.removed`, `meta.saved` (not files a merge brought in). The\ncache-sync kinds stay on the per-monorepo stream.\n\nOrder and cursor: every line has the display key `(at, monorepo, seq)`.\n`next` is an opaque cursor, \"older than this line\"; pass it back as\n`cursor`. It is stable while lines arrive: new lines are newer, and a\nlate or repeated delivery never moves a line already paged past.\n\nWhere lines come from: each person has a feed (a Durable Object). A\nmonorepo of 100 members or fewer copies its lines into its members'\nfeeds; a larger one is read from its own log on every page. A feed\nkeeps 7 days (at most 1,000,000 lines); older pages, and a monorepo\nthe feed joined with a partial history, are read from the monorepos'\nown logs, which keep everything. At most 20 logs are read for one\npage, most recently active first; the rest are named in `skipped`\n(narrow with `in` to read one of them).\n\nWith `Upgrade: websocket`: one socket per person, for every small\nmonorepo. Send `{\"type\": \"hello\", \"since\": <landed>}` (since\noptional): the feed replays the lines that landed after it (at most\n500, else one `resync` frame: refetch the first page), then\n`{\"type\": \"caught_up\", \"landed\"}`. `landed` is the feed's own arrival\nnumber, not a time: keep the highest seen and send it on reconnect.\nLive frames:\n- `{\"type\": \"event\", \"landed\", \"event\": FeedEvent}`: place it by its\n  display key. The same line can arrive again with a fuller\n  `summary` (a push's commit count); replace by `(monorepo_id, seq)`.\n- `{\"type\": \"summary\", \"monorepo\", \"monorepo_id\", \"summary\": FeedSummary}`.\n- `{\"type\": \"needs\", \"monorepo\", \"monorepo_id\", \"changed\": [{repo, branch, state}], \"more\"}`:\n  branches that entered or left \"needs you\" (`state` null: left);\n  `more`: over 50 changed, refetch listNeeds.\n  A large monorepo sends `summary` and `needs` only when it crosses\n  100 members (either way); its line is read again with the list.\n- `{\"type\": \"joined\", \"monorepo\", \"monorepo_id\", \"large\"}`, `{\"type\": \"left\", \"monorepo\", \"monorepo_id\"}`:\n  refetch the first page and the list.\n- `{\"type\": \"contributed\", \"monorepo\", \"monorepo_id\", \"hour\", \"kind\", \"n\"}`:\n  your own work was counted (`kind` commit, branch or review; `hour`\n  the UTC hour, `2026-10-03T17`), for work of the last two days;\n  refetch getContributions. Large monorepos send it here too.\n- `{\"type\": \"error\", \"message\"}`: the frame sent was not understood\n  (anything but `hello` or the text `ping`). Nothing changed; the\n  socket stays open.\nEvery frame's `monorepo` is the `owner/name` label (as the index last\nheard it; a rename shows within a minute) and `monorepo_id` the id.\nThe text `ping` gets `pong`. Lines of large monorepos do not come\nhere: open their own event socket with `topics=feed` (the home opens\nat most 5; it refreshes the others every 60 s).\n\nCookie or Bearer. With the cookie, the upgrade must carry Pando's own\n`Origin` (403 `bad_origin`).\n"
---

> Documentation Index
> Fetch the complete documentation index at: https://docs.gitpando.com/llms.txt
> Use this file to discover all available pages before exploring further.

Path: Pando API › monorepos

`GET /v1/events`

The web home's log: what happened in every monorepo the person belongs
to, newest first, each line with its `monorepo`. People, machines and
worktrees only: no agent field.

Lines are the activity kinds: `push`, `branch.opened`,
`branch.deleted`, `merged`, `review`, `status` (only when a check's
state changes), `worktree.reported` (a new worktree only),
`worktree.removed`, `meta.saved` (not files a merge brought in). The
cache-sync kinds stay on the per-monorepo stream.

Order and cursor: every line has the display key `(at, monorepo, seq)`.
`next` is an opaque cursor, "older than this line"; pass it back as
`cursor`. It is stable while lines arrive: new lines are newer, and a
late or repeated delivery never moves a line already paged past.

Where lines come from: each person has a feed (a Durable Object). A
monorepo of 100 members or fewer copies its lines into its members'
feeds; a larger one is read from its own log on every page. A feed
keeps 7 days (at most 1,000,000 lines); older pages, and a monorepo
the feed joined with a partial history, are read from the monorepos'
own logs, which keep everything. At most 20 logs are read for one
page, most recently active first; the rest are named in `skipped`
(narrow with `in` to read one of them).

With `Upgrade: websocket`: one socket per person, for every small
monorepo. Send `{"type": "hello", "since": <landed>}` (since
optional): the feed replays the lines that landed after it (at most
500, else one `resync` frame: refetch the first page), then
`{"type": "caught_up", "landed"}`. `landed` is the feed's own arrival
number, not a time: keep the highest seen and send it on reconnect.
Live frames:
- `{"type": "event", "landed", "event": FeedEvent}`: place it by its
  display key. The same line can arrive again with a fuller
  `summary` (a push's commit count); replace by `(monorepo_id, seq)`.
- `{"type": "summary", "monorepo", "monorepo_id", "summary": FeedSummary}`.
- `{"type": "needs", "monorepo", "monorepo_id", "changed": [{repo, branch, state}], "more"}`:
  branches that entered or left "needs you" (`state` null: left);
  `more`: over 50 changed, refetch listNeeds.
  A large monorepo sends `summary` and `needs` only when it crosses
  100 members (either way); its line is read again with the list.
- `{"type": "joined", "monorepo", "monorepo_id", "large"}`, `{"type": "left", "monorepo", "monorepo_id"}`:
  refetch the first page and the list.
- `{"type": "contributed", "monorepo", "monorepo_id", "hour", "kind", "n"}`:
  your own work was counted (`kind` commit, branch or review; `hour`
  the UTC hour, `2026-10-03T17`), for work of the last two days;
  refetch getContributions. Large monorepos send it here too.
- `{"type": "error", "message"}`: the frame sent was not understood
  (anything but `hello` or the text `ping`). Nothing changed; the
  socket stays open.
Every frame's `monorepo` is the `owner/name` label (as the index last
heard it; a rename shows within a minute) and `monorepo_id` the id.
The text `ping` gets `pong`. Lines of large monorepos do not come
here: open their own event socket with `topics=feed` (the home opens
at most 5; it refreshes the others every 60 s).

Cookie or Bearer. With the cookie, the upgrade must carry Pando's own
`Origin` (403 `bad_origin`).

## Authentication

Requires one of the following:

- `credential`, http, header `Authorization`
- `session`, apiKey, in cookie

## Query parameters

- `listActivity.query.cursor` (string, optional) — A `next` from an earlier page.
- `listActivity.query.limit` (integer, optional)
  - min 1; max 200; default `50`
- `listActivity.query.in` (string, optional) — One monorepo's `owner/name` (an old name works too). The home narrows by monorepo only.
- `listActivity.query.owner` (string, optional) — Only the person's monorepos under this owner (an org's page shows its activity this way). Not with `in` (400). An owner with none of the person's monorepos gives an empty page.
  - pattern `^[a-z0-9](?:[a-z0-9]|-(?=[a-z0-9])){0,38}$`

## Code samples

### cURL

```curl
curl --request GET \
  --url https://app.gitpando.com/v1/events \
  --header 'Authorization: Bearer <token>'
```

### TypeScript

```typescript
const url = 'https://app.gitpando.com/v1/events';
const options = {method: 'GET', headers: {Authorization: 'Bearer <token>'}};

fetch(url, options)
  .then(res => res.json())
  .then(json => console.log(json))
  .catch(err => console.error(err));
```

### Python

```python
import requests

url = "https://app.gitpando.com/v1/events"

headers = {"Authorization": "Bearer <token>"}

response = requests.get(url, headers=headers)

print(response.text)
```

## Responses

### 101

The person's WebSocket.

### 200

A page, newest first

#### Example

```json
{
  "events": [
    {
      "monorepo": "string",
      "monorepo_id": "string",
      "seq": 0,
      "at": "2019-08-24T14:15:22Z",
      "kind": "push",
      "repo": "string",
      "repo_path": "string",
      "branch": "string",
      "actor": "string",
      "machine": "string",
      "summary": {}
    }
  ],
  "next": "string",
  "from_logs": true,
  "skipped": [
    "string"
  ]
}
```

- `listActivity.response.200.events` (array<object>, required)
  - `listActivity.response.200.events.monorepo` (string, required) — `owner/name`, as it is when the line is read.
  - `listActivity.response.200.events.monorepo_id` (string, required) — The monorepo's id. Pages sort by (at, monorepo_id, seq).
  - `listActivity.response.200.events.seq` (integer, required) — The monorepo's event seq. `(monorepo_id, seq)` is the line's identity.
  - `listActivity.response.200.events.at` (string, required)
    - format `date-time`
  - `listActivity.response.200.events.kind` (string, required)
    - one of `"push"`, `"branch.opened"`, `"branch.deleted"`, `"merged"`, `"review"`, `"status"`, `"worktree.reported"`, `"worktree.removed"`, `"meta.saved"`, `"issue.opened"`, `"issue.commented"`, `"issue.closed"`, `"issue.reopened"`
  - `listActivity.response.200.events.repo` (string | null, required) — The repo's name in URLs (`meta` for the meta layer).
  - `listActivity.response.200.events.repo_path` (string | null, required) — `projects/web`; `.` for the meta layer and a plain repo.
  - `listActivity.response.200.events.branch` (string | null, required)
  - `listActivity.response.200.events.actor` (string | null, required) — The person (a login). A meta.saved line from before versions kept the login has the author's name.
  - `listActivity.response.200.events.machine` (string | null, required) — The credential's name (the machine or sandbox).
  - `listActivity.response.200.events.summary` (object | null, required) — Per kind: - push, branch.opened: `commits` (new commits in this push; for a new branch, those not on main), `head_title`, `head`, `before`. `commits` and `head_title` arrive a moment after the push (the branch-facts job adds them); null when they could not be read. `commits_more`: true when a first push of main had more than 250. - branch.deleted: `before`. - merged: `merge_commit`, `forced`, `title` (the branch's), `head`, `closes` (the issues it closes, as MergeResult has them). - review: `verdict`, `comments` (a count), `head`. - status: `context`, `state`, `description`, `target_url`, `sha`, `duration_s` (from the run's first `pending` to this state; null without one), `app` (the CI service that reported it, null when a member posted it; `actor` is then that member, else null). - worktree.reported, worktree.removed: `path`, `worktree` (its id). - meta.saved: `path`, `version`, `deleted`. - issue.opened, issue.commented, issue.closed (by a person; a merge's close is on its merged line), issue.reopened: `number`, `title`, `reason` (closed: completed or not_planned), `excerpt` (commented: the first 140 characters). The line's repo is the issue's project.
- `listActivity.response.200.next` (string | null, required) — The cursor for the next (older) page; null at the end.
- `listActivity.response.200.from_logs` (boolean, required) — True when the page went past the feed's window and read monorepos' own logs.
- `listActivity.response.200.skipped` (array<string>, required) — Monorepos (`owner/name`) not read for this page (over 20 logs); narrow with `in` to read one.

### 400

An error

#### Example

```json
{
  "error": {
    "code": "string",
    "message": "string",
    "paths": [
      "string"
    ],
    "checks": {
      "sha": "string",
      "state": "success",
      "required": [
        "string"
      ],
      "checks_unknown": "string",
      "missing": [
        "string"
      ],
      "statuses": [
        {
          "sha": "string",
          "context": "string",
          "state": "pending",
          "target_url": "string",
          "description": "string",
          "creator": "string",
          "app": "string",
          "created_at": "2019-08-24T14:15:22Z"
        }
      ]
    }
  }
}
```

- `listActivity.response.400.error` (object, required)
  - `listActivity.response.400.error.code` (string, required)
  - `listActivity.response.400.error.message` (string, required)
  - `listActivity.response.400.error.paths` (array<string>, optional)
  - `listActivity.response.400.error.checks` (object, optional) — GitHub's combined status, plus the required contexts and which are missing.
    - `listActivity.response.400.error.checks.sha` (string | null, required)
    - `listActivity.response.400.error.checks.state` (string, required) — `success` when the merge gate passes; `missing` when a required context has not reported; `unknown` while the required checks are (an unforced merge is refused).
      - one of `"success"`, `"pending"`, `"failure"`, `"error"`, `"missing"`, `"unknown"`
    - `listActivity.response.400.error.checks.required` (array | null, required) — Null while unknown.
    - `listActivity.response.400.error.checks.checks_unknown` (string | null, required) — Why the required checks are unknown, in plain words: "Pando couldn't read this monorepo's required checks; try again", or "pando.toml line 3: expected comma or end of structure; fix it on main, or merge with force". Null otherwise.
    - `listActivity.response.400.error.checks.statuses` (array<object>, required) — The latest status per context.
      - `listActivity.response.400.error.checks.statuses.sha` (string, required)
      - `listActivity.response.400.error.checks.statuses.context` (string, required)
      - `listActivity.response.400.error.checks.statuses.state` (string, required)
        - one of `"pending"`, `"success"`, `"failure"`, `"error"`
      - `listActivity.response.400.error.checks.statuses.created_at` (string, required)
        - format `date-time`
      - `listActivity.response.400.error.checks.statuses.target_url` (string | null, optional)
      - `listActivity.response.400.error.checks.statuses.description` (string | null, optional)
      - `listActivity.response.400.error.checks.statuses.creator` (string, optional) — The member who posted it (a login); `app:<slug>` for a status posted with a status token or by a CI integration; `hook:<hook id>` for a status token from a web hook with no app.
      - `listActivity.response.400.error.checks.statuses.app` (string | null, optional) — The CI service that reported it (`buildkite`, `github-actions`, ...), stamped by Pando from a status token or a CI integration, never from the body or `target_url`. Null when a member posted it with their own credential. The web shows the service's mark for a slug it knows.
    - `listActivity.response.400.error.checks.missing` (array<string>, optional)

### 401

An error

#### Example

```json
{
  "error": {
    "code": "string",
    "message": "string",
    "paths": [
      "string"
    ],
    "checks": {
      "sha": "string",
      "state": "success",
      "required": [
        "string"
      ],
      "checks_unknown": "string",
      "missing": [
        "string"
      ],
      "statuses": [
        {
          "sha": "string",
          "context": "string",
          "state": "pending",
          "target_url": "string",
          "description": "string",
          "creator": "string",
          "app": "string",
          "created_at": "2019-08-24T14:15:22Z"
        }
      ]
    }
  }
}
```

- `listActivity.response.401.error` (object, required)
  - `listActivity.response.401.error.code` (string, required)
  - `listActivity.response.401.error.message` (string, required)
  - `listActivity.response.401.error.paths` (array<string>, optional)
  - `listActivity.response.401.error.checks` (object, optional) — GitHub's combined status, plus the required contexts and which are missing.
    - `listActivity.response.401.error.checks.sha` (string | null, required)
    - `listActivity.response.401.error.checks.state` (string, required) — `success` when the merge gate passes; `missing` when a required context has not reported; `unknown` while the required checks are (an unforced merge is refused).
      - one of `"success"`, `"pending"`, `"failure"`, `"error"`, `"missing"`, `"unknown"`
    - `listActivity.response.401.error.checks.required` (array | null, required) — Null while unknown.
    - `listActivity.response.401.error.checks.checks_unknown` (string | null, required) — Why the required checks are unknown, in plain words: "Pando couldn't read this monorepo's required checks; try again", or "pando.toml line 3: expected comma or end of structure; fix it on main, or merge with force". Null otherwise.
    - `listActivity.response.401.error.checks.statuses` (array<object>, required) — The latest status per context.
      - `listActivity.response.401.error.checks.statuses.sha` (string, required)
      - `listActivity.response.401.error.checks.statuses.context` (string, required)
      - `listActivity.response.401.error.checks.statuses.state` (string, required)
        - one of `"pending"`, `"success"`, `"failure"`, `"error"`
      - `listActivity.response.401.error.checks.statuses.created_at` (string, required)
        - format `date-time`
      - `listActivity.response.401.error.checks.statuses.target_url` (string | null, optional)
      - `listActivity.response.401.error.checks.statuses.description` (string | null, optional)
      - `listActivity.response.401.error.checks.statuses.creator` (string, optional) — The member who posted it (a login); `app:<slug>` for a status posted with a status token or by a CI integration; `hook:<hook id>` for a status token from a web hook with no app.
      - `listActivity.response.401.error.checks.statuses.app` (string | null, optional) — The CI service that reported it (`buildkite`, `github-actions`, ...), stamped by Pando from a status token or a CI integration, never from the body or `target_url`. Null when a member posted it with their own credential. The web shows the service's mark for a slug it knows.
    - `listActivity.response.401.error.checks.missing` (array<string>, optional)

### 403

An error

#### Example

```json
{
  "error": {
    "code": "string",
    "message": "string",
    "paths": [
      "string"
    ],
    "checks": {
      "sha": "string",
      "state": "success",
      "required": [
        "string"
      ],
      "checks_unknown": "string",
      "missing": [
        "string"
      ],
      "statuses": [
        {
          "sha": "string",
          "context": "string",
          "state": "pending",
          "target_url": "string",
          "description": "string",
          "creator": "string",
          "app": "string",
          "created_at": "2019-08-24T14:15:22Z"
        }
      ]
    }
  }
}
```

- `listActivity.response.403.error` (object, required)
  - `listActivity.response.403.error.code` (string, required)
  - `listActivity.response.403.error.message` (string, required)
  - `listActivity.response.403.error.paths` (array<string>, optional)
  - `listActivity.response.403.error.checks` (object, optional) — GitHub's combined status, plus the required contexts and which are missing.
    - `listActivity.response.403.error.checks.sha` (string | null, required)
    - `listActivity.response.403.error.checks.state` (string, required) — `success` when the merge gate passes; `missing` when a required context has not reported; `unknown` while the required checks are (an unforced merge is refused).
      - one of `"success"`, `"pending"`, `"failure"`, `"error"`, `"missing"`, `"unknown"`
    - `listActivity.response.403.error.checks.required` (array | null, required) — Null while unknown.
    - `listActivity.response.403.error.checks.checks_unknown` (string | null, required) — Why the required checks are unknown, in plain words: "Pando couldn't read this monorepo's required checks; try again", or "pando.toml line 3: expected comma or end of structure; fix it on main, or merge with force". Null otherwise.
    - `listActivity.response.403.error.checks.statuses` (array<object>, required) — The latest status per context.
      - `listActivity.response.403.error.checks.statuses.sha` (string, required)
      - `listActivity.response.403.error.checks.statuses.context` (string, required)
      - `listActivity.response.403.error.checks.statuses.state` (string, required)
        - one of `"pending"`, `"success"`, `"failure"`, `"error"`
      - `listActivity.response.403.error.checks.statuses.created_at` (string, required)
        - format `date-time`
      - `listActivity.response.403.error.checks.statuses.target_url` (string | null, optional)
      - `listActivity.response.403.error.checks.statuses.description` (string | null, optional)
      - `listActivity.response.403.error.checks.statuses.creator` (string, optional) — The member who posted it (a login); `app:<slug>` for a status posted with a status token or by a CI integration; `hook:<hook id>` for a status token from a web hook with no app.
      - `listActivity.response.403.error.checks.statuses.app` (string | null, optional) — The CI service that reported it (`buildkite`, `github-actions`, ...), stamped by Pando from a status token or a CI integration, never from the body or `target_url`. Null when a member posted it with their own credential. The web shows the service's mark for a slug it knows.
    - `listActivity.response.403.error.checks.missing` (array<string>, optional)

### 404

An error

#### Example

```json
{
  "error": {
    "code": "string",
    "message": "string",
    "paths": [
      "string"
    ],
    "checks": {
      "sha": "string",
      "state": "success",
      "required": [
        "string"
      ],
      "checks_unknown": "string",
      "missing": [
        "string"
      ],
      "statuses": [
        {
          "sha": "string",
          "context": "string",
          "state": "pending",
          "target_url": "string",
          "description": "string",
          "creator": "string",
          "app": "string",
          "created_at": "2019-08-24T14:15:22Z"
        }
      ]
    }
  }
}
```

- `listActivity.response.404.error` (object, required)
  - `listActivity.response.404.error.code` (string, required)
  - `listActivity.response.404.error.message` (string, required)
  - `listActivity.response.404.error.paths` (array<string>, optional)
  - `listActivity.response.404.error.checks` (object, optional) — GitHub's combined status, plus the required contexts and which are missing.
    - `listActivity.response.404.error.checks.sha` (string | null, required)
    - `listActivity.response.404.error.checks.state` (string, required) — `success` when the merge gate passes; `missing` when a required context has not reported; `unknown` while the required checks are (an unforced merge is refused).
      - one of `"success"`, `"pending"`, `"failure"`, `"error"`, `"missing"`, `"unknown"`
    - `listActivity.response.404.error.checks.required` (array | null, required) — Null while unknown.
    - `listActivity.response.404.error.checks.checks_unknown` (string | null, required) — Why the required checks are unknown, in plain words: "Pando couldn't read this monorepo's required checks; try again", or "pando.toml line 3: expected comma or end of structure; fix it on main, or merge with force". Null otherwise.
    - `listActivity.response.404.error.checks.statuses` (array<object>, required) — The latest status per context.
      - `listActivity.response.404.error.checks.statuses.sha` (string, required)
      - `listActivity.response.404.error.checks.statuses.context` (string, required)
      - `listActivity.response.404.error.checks.statuses.state` (string, required)
        - one of `"pending"`, `"success"`, `"failure"`, `"error"`
      - `listActivity.response.404.error.checks.statuses.created_at` (string, required)
        - format `date-time`
      - `listActivity.response.404.error.checks.statuses.target_url` (string | null, optional)
      - `listActivity.response.404.error.checks.statuses.description` (string | null, optional)
      - `listActivity.response.404.error.checks.statuses.creator` (string, optional) — The member who posted it (a login); `app:<slug>` for a status posted with a status token or by a CI integration; `hook:<hook id>` for a status token from a web hook with no app.
      - `listActivity.response.404.error.checks.statuses.app` (string | null, optional) — The CI service that reported it (`buildkite`, `github-actions`, ...), stamped by Pando from a status token or a CI integration, never from the body or `target_url`. Null when a member posted it with their own credential. The web shows the service's mark for a slug it knows.
    - `listActivity.response.404.error.checks.missing` (array<string>, optional)


Source: https://docs.gitpando.com/api/monorepos/listActivity/index.md
