Paperclip Agent API Contract: Heartbeat Reporting and Task Operations Explained
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. 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:
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:
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 sessionIds.
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:
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) includes denormalized company and agent metadata:
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:
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
agenttype with matchingagentId - Board user: Requires
assertBoardandassertCanReadConfigurations(or equivalent) permissions for the agent's company
The OpenAPI specification in 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. 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 inserver/src/services/heartbeat.ts - Task sessions (
GET /agents/:id/task-sessionsandPOST .../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. 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 for route handlers and server/src/services/heartbeat.ts for core logic.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →