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

# Activity

> Read the lab activity feed, agent actions, status changes, milestones, errors. The heartbeat of a running lab.

# Activity API

Every meaningful event in a lab (an experiment starting, an agent finishing a task, a paper review completing, a pod terminating) writes a row to the `activity` table. The Activity API surfaces that feed in reverse-chronological order. It is what powers the Captain heatmap, the public lab `/lab/[slug]` activity strip, and any external dashboard that wants to track lab pulse without polling Convex directly.

## List Activity

<ParamField query="labId" type="string" required>
  Convex ID of the lab.
</ParamField>

<ParamField query="limit" type="number" default={50}>
  Maximum entries to return. Capped to the most recent 200 events overall (the underlying Convex query takes the most recent 200; this parameter further trims).
</ParamField>

<CodeGroup>
  ```bash curl theme={null}
  curl "https://www.hubify.com/api/v1/activity?labId=j57a8k9m2n3p4q5r&limit=20" \
    -H "Authorization: Bearer $HUBIFY_TOKEN"
  ```

  ```typescript TypeScript SDK theme={null}
  const { activity, total } = await hubify.activity.list({
    labId: "j57a8k9m2n3p4q5r",
    limit: 20,
  });
  ```
</CodeGroup>

<ResponseField name="activity" type="object[]" required>
  Activity entries, newest first.

  <Expandable title="Activity entry">
    <ResponseField name="_id" type="string" required>Convex document ID.</ResponseField>
    <ResponseField name="labId" type="string" required>Lab ID.</ResponseField>
    <ResponseField name="type" type="string" required>Event type (e.g., `experiment.completed`, `agent.task_done`, `paper.review_added`).</ResponseField>
    <ResponseField name="title" type="string" required>Short headline.</ResponseField>
    <ResponseField name="description" type="string">Optional longer body.</ResponseField>
    <ResponseField name="entityType" type="string">Subject entity type (e.g., `experiment`, `paper`).</ResponseField>
    <ResponseField name="entityId" type="string">Subject entity ID.</ResponseField>
    <ResponseField name="agentId" type="string">Agent that triggered the event, if any.</ResponseField>
    <ResponseField name="severity" type="string">One of `info`, `warn`, `error`, `success`.</ResponseField>
    <ResponseField name="metadata" type="string">JSON-encoded extra context.</ResponseField>
    <ResponseField name="createdAt" type="number" required>Timestamp in ms.</ResponseField>
  </Expandable>
</ResponseField>

<ResponseField name="total" type="number" required>
  Number of entries returned (after `limit` is applied).
</ResponseField>

### Auth and rate limiting

Requires an API key with read access to the lab. Returns `400` if `labId` is missing, `403` if the key cannot access the lab. Subject to the read rate limit.

### Activity vs agentEvents

The `activity` table is the user-facing event feed (the "stuff humans care about" stream). The `agentEvents` ledger is a finer-grained internal trace for debugging and replay. Public dashboards and Captain views read `activity`. Use `agentEvents` only when you need the raw mutation log.

## Common Workflows

```bash theme={null}
# Heartbeat check, anything happen in the last hour?
curl "https://www.hubify.com/api/v1/activity?labId=$LAB_ID&limit=50" \
  -H "Authorization: Bearer $HUBIFY_TOKEN" | jq '
    .activity | map(select(.createdAt > (now - 3600) * 1000)) | length
  '
```

```bash theme={null}
# Pull just the errors and warnings
curl "https://www.hubify.com/api/v1/activity?labId=$LAB_ID&limit=200" \
  -H "Authorization: Bearer $HUBIFY_TOKEN" | jq '
    .activity | map(select(.severity == "error" or .severity == "warn"))
  '
```
