# Paperclip Agent API Contract: Heartbeat Reporting and Task Operations Explained

> Explore the Paperclip agent API contract for heartbeat reporting and task operations. Understand REST endpoints, TypeScript interfaces, and OpenAPI specs for managing agent status and tasks.

- Repository: [Paperclip/paperclip](https://github.com/paperclipai/paperclip)
- Tags: api-reference
- Published: 2026-08-16

---

**Paperclip's agent API contract exposes REST endpoints under `/api` for heartbeat status reporting, runtime state management, and task session operations, with all payloads defined in TypeScript interfaces and documented in the OpenAPI spec.**

The Paperclip control-plane uses a strict contract to coordinate distributed agents. This article breaks down the exact API endpoints, request/response shapes, and source locations that define how agents report their **heartbeat status** and manage **task sessions** during execution.

## Core Runtime State and Heartbeat Endpoints

All agent-facing endpoints reside in [`server/src/routes/agents.ts`](https://github.com/paperclipai/paperclip/blob/main/server/src/routes/agents.ts). The heartbeat system revolves around the **runtime state** — a singleton record per agent that tracks liveness, scheduling eligibility, and task session bindings.

### GET /agents/:id/runtime-state

Retrieve the authoritative heartbeat metadata for an agent. This endpoint returns the `RuntimeState` interface defined in [`server/src/services/heartbeat.ts`](https://github.com/paperclipai/paperclip/blob/main/server/src/services/heartbeat.ts):

```typescript
export interface RuntimeState {
  agentId: string;
  companyId: string;
  status: "idle" | "paused" | "running" | "error";
  intervalSec: number;            // heartbeat interval (seconds)
  enabled: boolean;               // true if heartbeat is configured
  lastHeartbeatAt: string | null; // ISO-8601 timestamp
  schedulerActive: boolean;       // true when both enabled and status-eligible
}

```

The `schedulerActive` boolean is computed — it is **true only when** `enabled === true` and `status` permits scheduling (typically `"idle"` or `"running"`). The `intervalSec` field controls how frequently the agent should emit heartbeats.

### GET /agents/:id/task-sessions

List all **task sessions** currently bound to the agent. Each session represents a long-running unit of work that survives across individual heartbeat pings. The response is an array of `TaskSession` objects:

```typescript
export interface TaskSession {
  sessionId: string;
  taskKey?: string | null;        // optional stable identifier for the task
  startedAt: string;
  lastSeenAt: string;
  status: "active" | "completed" | "failed";
  sessionParamsJson?: string | null; // redacted payload
}

```

The `lastSeenAt` timestamp updates when the agent reports progress on this specific session. The `taskKey` field allows idempotent session creation — agents can reference stable task names rather than server-assigned `sessionId`s.

## Task Session Lifecycle Management

### POST /agents/:id/runtime-state/reset-session

**Reset or create a task session** for the agent. This endpoint is critical for recovery scenarios — it clears stale sessions and optionally initializes a new one with a specific `taskKey`.

| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
| `taskKey` | `string` | No | Stable identifier for the task; omit to reset without binding |

The response returns the updated `RuntimeState`, reflecting any session changes immediately.

**Example: Resetting a session with Node.js fetch:**

```javascript
async function resetSession(agentId, token, taskKey = null) {
  const body = taskKey ? { taskKey } : {};
  const res = await fetch(
    `https://your-paperclip-instance/api/agents/${agentId}/runtime-state/reset-session`,
    {
      method: "POST",
      headers: { 
        "Content-Type": "application/json", 
        Authorization: `Bearer ${token}` 
      },
      body: JSON.stringify(body),
    }
  );
  return await res.json(); // → updated RuntimeState
}

```

## Administrative Heartbeat Monitoring

### GET /instance/scheduler-heartbeats

Returns a **cross-tenant view** of all agents with scheduler-driven heartbeats enabled. This endpoint is restricted to admin callers and powers the system health dashboard.

The response shape `InstanceSchedulerHeartbeatAgent` (also in [`server/src/routes/agents.ts`](https://github.com/paperclipai/paperclip/blob/main/server/src/routes/agents.ts)) includes denormalized company and agent metadata:

```typescript
export interface InstanceSchedulerHeartbeatAgent {
  id: string;
  companyId: string;
  companyName: string;
  companyIssuePrefix: string;
  agentName: string;
  agentUrlKey: string;
  role: string;
  title: string;
  status: string;
  adapterType: string;
  intervalSec: number;
  heartbeatEnabled: boolean;
  schedulerActive: boolean;
  lastHeartbeatAt: string | null;
}

```

**Example: Fetching global heartbeat status:**

```javascript
async function listSchedulerHeartbeats(adminToken) {
  const res = await fetch(
    `https://your-paperclip-instance/api/instance/scheduler-heartbeats`,
    { headers: { Authorization: `Bearer ${adminToken}` } }
  );
  return await res.json(); // → InstanceSchedulerHeartbeatAgent[]
}

```

## Agent Control Operations

Three **state mutation endpoints** manage agent availability without modifying heartbeat configuration directly:

| Endpoint | Effect | Resulting Status |
|----------|--------|----------------|
| `POST /agents/:id/pause` | Stops heartbeats and terminates running tasks | `"paused"` |
| `POST /agents/:id/resume` | Re-enables heartbeat processing | `"idle"` |
| `POST /agents/:id/clear-error` | Acknowledges and clears terminal error state | Previous non-error status |

All three return the updated `Agent` object. The `pause`/`resume` pair is preferred over directly mutating `enabled` — it preserves the configuration while temporarily disabling execution.

## Authentication and Authorization Rules

Every endpoint enforces **actor-based access control** as implemented in the route handlers:

- **Agent actor**: The bearer token must identify as `agent` type with matching `agentId`
- **Board user**: Requires `assertBoard` and `assertCanReadConfigurations` (or equivalent) permissions for the agent's company

The OpenAPI specification in [`server/src/routes/openapi.ts`](https://github.com/paperclipai/paperclip/blob/main/server/src/routes/openapi.ts) documents these constraints, though runtime enforcement happens in the Express route handlers.

## Client-Side API Wrapper

The Paperclip UI consumes these same endpoints through [`ui/src/api/heartbeats.ts`](https://github.com/paperclipai/paperclip/blob/main/ui/src/api/heartbeats.ts). This module mirrors the server contract, ensuring type consistency across the stack. When implementing custom agents, reference both the server source and this client wrapper to validate payload shapes.

## Summary

- **Runtime state** (`GET /agents/:id/runtime-state`) is the single source of truth for agent heartbeat metadata, defined in [`server/src/services/heartbeat.ts`](https://github.com/paperclipai/paperclip/blob/main/server/src/services/heartbeat.ts)
- **Task sessions** (`GET /agents/:id/task-sessions` and `POST .../reset-session`) isolate long-running work from heartbeat pings
- **Scheduler heartbeats** (`GET /instance/scheduler-heartbeats`) provides admin visibility across all tenants
- **Control operations** (`pause`, `resume`, `clear-error`) modify runtime behavior without changing configuration
- All timestamps are **ISO-8601 strings**, all payloads are **JSON**, and all endpoints live under `/api`

## Frequently Asked Questions

### What determines if an agent's scheduler is considered active?

The `schedulerActive` field in `RuntimeState` is **true only when** `enabled === true` and the `status` allows scheduling (typically `"idle"` or `"running"`). A paused, errored, or explicitly disabled agent will show `schedulerActive: false` even if the heartbeat interval is configured.

### How do task sessions differ from heartbeat reports?

A **heartbeat report** is a periodic ping that updates `lastHeartbeatAt`. A **task session** is a durable scope of work with its own `sessionId`, `status`, and `lastSeenAt` timestamp. One agent may have zero or multiple active sessions while maintaining a single heartbeat stream. Sessions survive transient network failures; heartbeats detect them.

### Can I reset a task session without specifying a taskKey?

Yes. The `POST /agents/:id/runtime-state/reset-session` endpoint accepts an empty body to simply clear any existing session. Omitting `taskKey` is useful for recovery scenarios where you want to force the agent into a clean state without binding it to new work immediately.

### Where is the API contract formally documented?

The OpenAPI specification is generated from [`server/src/routes/openapi.ts`](https://github.com/paperclipai/paperclip/blob/main/server/src/routes/openapi.ts). This file aggregates route definitions and produces machine-readable documentation for all heartbeat and task session endpoints. For implementation details, examine [`server/src/routes/agents.ts`](https://github.com/paperclipai/paperclip/blob/main/server/src/routes/agents.ts) for route handlers and [`server/src/services/heartbeat.ts`](https://github.com/paperclipai/paperclip/blob/main/server/src/services/heartbeat.ts) for core logic.