PAP Agents API Reference
This document provides comprehensive API documentation for creating and managing PAP agents.
Base URL
https://plugged.in/api/agents
Authentication
All endpoints require API key authentication via the Authorization header:
Authorization: Bearer YOUR_API_KEY
API Keys: Generate API keys from your Plugged.in project settings. Keys are scoped to profiles within projects.
Endpoints
List Agents
Retrieve all agents for the authenticated user’s active profile.
curl https://plugged.in/api/agents \
-H "Authorization: Bearer YOUR_API_KEY"
Response
[
{
"uuid": "123e4567-e89b-12d3-a456-426614174000",
"name": "my-agent",
"dns_name": "my-agent.is.plugged.in",
"state": "ACTIVE",
"kubernetes_namespace": "agents",
"kubernetes_deployment": "my-agent",
"created_at": "2025-11-13T08:00:00Z",
"provisioned_at": "2025-11-13T08:00:15Z",
"activated_at": "2025-11-13T08:00:45Z",
"terminated_at": null,
"last_heartbeat_at": "2025-11-13T08:05:30Z",
"metadata": {
"description": "My autonomous agent",
"image": "nginxinc/nginx-unprivileged:alpine",
"resources": {
"cpu_request": "100m",
"memory_request": "256Mi",
"cpu_limit": "1000m",
"memory_limit": "1Gi"
}
}
}
]
Response Fields
| Field | Type | Description |
|---|
uuid | UUID | Unique agent identifier |
name | string | DNS-safe agent name |
dns_name | string | Full DNS hostname ({name}.is.plugged.in) |
state | enum | Current lifecycle state (see Lifecycle) |
kubernetes_namespace | string | Kubernetes namespace (typically “agents”) |
kubernetes_deployment | string | Deployment name in Kubernetes |
created_at | timestamp | When agent was created |
provisioned_at | timestamp | When Kubernetes resources were deployed |
activated_at | timestamp | When agent sent first heartbeat |
terminated_at | timestamp | When agent was terminated (null if active) |
last_heartbeat_at | timestamp | Last heartbeat received (null if no heartbeats yet) |
metadata | object | Agent configuration and metadata |
Create Agent
Deploy a new PAP agent to Kubernetes infrastructure.
curl -X POST https://plugged.in/api/agents \
-H "Authorization: Bearer YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"name": "my-agent",
"description": "My autonomous agent",
"resources": {
"cpu_request": "100m",
"memory_request": "256Mi",
"cpu_limit": "1000m",
"memory_limit": "1Gi"
}
}'
Request Body
| Field | Type | Required | Description |
|---|
name | string | Yes | DNS-safe agent name (lowercase, alphanumeric, hyphens) |
description | string | No | Human-readable description |
image | string | No | Container image (default: nginxinc/nginx-unprivileged:alpine) |
resources | object | No | Resource requests and limits |
resources.cpu_request | string | No | CPU request (e.g., “100m”, default: “100m”) |
resources.memory_request | string | No | Memory request (e.g., “256Mi”, default: “256Mi”) |
resources.cpu_limit | string | No | CPU limit (e.g., “1000m”, default: “1000m”) |
resources.memory_limit | string | No | Memory limit (e.g., “1Gi”, default: “1Gi”) |
DNS-Safe Names: Agent names must match the pattern ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$. Invalid names will return HTTP 400.
Response (Success)
{
"agent": {
"uuid": "123e4567-e89b-12d3-a456-426614174000",
"name": "my-agent",
"dns_name": "my-agent.is.plugged.in",
"state": "NEW",
"kubernetes_namespace": "agents",
"kubernetes_deployment": "my-agent",
"created_at": "2025-11-13T08:00:00Z",
"provisioned_at": null,
"activated_at": null,
"terminated_at": null,
"last_heartbeat_at": null,
"metadata": {
"description": "My autonomous agent",
"resources": {
"cpu_request": "100m",
"memory_request": "256Mi",
"cpu_limit": "1000m",
"memory_limit": "1Gi"
}
}
},
"deployment": {
"success": true,
"message": "Agent my-agent deployed successfully",
"deploymentName": "my-agent"
}
}
Error Responses
| Status | Error | Description |
|---|
| 400 | Name is required | Missing name field |
| 400 | Name must be DNS-safe: lowercase alphanumeric and hyphens only | Invalid name format |
| 401 | Unauthorized | Missing or invalid API key |
| 409 | Agent with this name already exists | Duplicate agent name in profile |
| 500 | Failed to create agent | Internal server error |
Get Agent Details
Retrieve comprehensive information about a specific agent, including recent heartbeats, metrics, and lifecycle events.
curl https://plugged.in/api/agents/AGENT_UUID \
-H "Authorization: Bearer YOUR_API_KEY"
Response
{
"agent": {
"uuid": "123e4567-e89b-12d3-a456-426614174000",
"name": "my-agent",
"dns_name": "my-agent.is.plugged.in",
"state": "ACTIVE",
"created_at": "2025-11-13T08:00:00Z",
"last_heartbeat_at": "2025-11-13T08:05:30Z",
"metadata": {...}
},
"recentHeartbeats": [
{
"id": "1001",
"agent_uuid": "123e4567-e89b-12d3-a456-426614174000",
"mode": "IDLE",
"uptime_seconds": 330,
"timestamp": "2025-11-13T08:05:30Z"
}
],
"recentMetrics": [
{
"id": "2001",
"agent_uuid": "123e4567-e89b-12d3-a456-426614174000",
"cpu_percent": 12,
"memory_mb": 128,
"requests_handled": 45,
"custom_metrics": {},
"timestamp": "2025-11-13T08:05:00Z"
}
],
"lifecycleEvents": [
{
"id": "3001",
"agent_uuid": "123e4567-e89b-12d3-a456-426614174000",
"event_type": "ACTIVATED",
"from_state": "PROVISIONED",
"to_state": "ACTIVE",
"metadata": {
"triggered_by": "system"
},
"timestamp": "2025-11-13T08:00:45Z"
}
],
"kubernetesStatus": {
"ready": true,
"replicas": 1,
"readyReplicas": 1,
"unavailableReplicas": 0,
"conditions": [
{
"type": "Available",
"status": "True",
"reason": "MinimumReplicasAvailable",
"message": "Deployment has minimum availability."
}
]
}
}
Response Fields
| Field | Type | Description |
|---|
agent | object | Core agent metadata (same as list response) |
recentHeartbeats | array | Last 10 heartbeats (ordered newest first) |
recentMetrics | array | Last 10 metric reports (ordered newest first) |
lifecycleEvents | array | Complete lifecycle event history (ordered newest first) |
kubernetesStatus | object | Real-time Kubernetes deployment status (null if not deployed) |
Error Responses
| Status | Error | Description |
|---|
| 401 | Unauthorized | Missing or invalid API key |
| 404 | Agent not found | Agent doesn’t exist or doesn’t belong to profile |
| 500 | Failed to fetch agent | Internal server error |
Delete Agent
Terminate and delete an agent, removing all Kubernetes resources.
curl -X DELETE https://plugged.in/api/agents/AGENT_UUID \
-H "Authorization: Bearer YOUR_API_KEY"
Response (Success)
{
"message": "Agent terminated successfully",
"kubernetes": {
"success": true,
"message": "Agent my-agent deleted successfully"
}
}
State Transition: User-initiated deletion transitions agent to TERMINATED state. Only the Station (control plane) can issue KILLED state.
Cleanup Operations
The DELETE operation removes:
- Kubernetes Deployment
- Kubernetes Service
- Kubernetes Ingress
- TLS Secret (Let’s Encrypt certificate)
The agent record remains in database with state=TERMINATED for audit purposes.
Error Responses
| Status | Error | Description |
|---|
| 401 | Unauthorized | Missing or invalid API key |
| 404 | Agent not found | Agent doesn’t exist or doesn’t belong to profile |
| 500 | Failed to delete agent | Internal server error |
Export Agent Data
Export complete agent data including telemetry history for backup or analysis.
curl -X POST https://plugged.in/api/agents/AGENT_UUID/export \
-H "Authorization: Bearer YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"include_telemetry": true,
"telemetry_limit": 1000
}'
Request Body
| Field | Type | Required | Description |
|---|
include_telemetry | boolean | No | Include heartbeat and metrics history (default: true) |
telemetry_limit | integer | No | Maximum telemetry records to include (default: 100) |
Response
Returns complete agent data including configuration, lifecycle events, and telemetry:
{
"agent": {
"uuid": "...",
"name": "my-agent",
"dns_name": "my-agent.is.plugged.in",
"state": "ACTIVE",
...
},
"heartbeats": [...],
"metrics": [...],
"lifecycle_events": [...],
"metadata": {
"exported_at": "2025-11-13T08:15:00Z",
"triggered_by": "user-uuid",
"total_heartbeats": 250,
"total_metrics": 240
}
}
Submit Heartbeat
Agents send heartbeats to signal liveness. Heartbeats contain ONLY liveness data (zombie prevention).
CRITICAL: Heartbeats must contain ONLY mode and uptime_seconds. Never include resource data (CPU, memory) in heartbeats—use the Metrics endpoint instead. This separation is the core of PAP’s zombie prevention.
curl -X POST https://plugged.in/api/agents/AGENT_UUID/heartbeat \
-H "Authorization: Bearer YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"mode": "IDLE",
"uptime_seconds": 330
}'
Request Body
| Field | Type | Required | Description |
|---|
mode | enum | Yes | Heartbeat mode: EMERGENCY (5s), IDLE (30s), SLEEP (15min) |
uptime_seconds | number | Yes | Agent uptime in seconds |
Heartbeat Intervals:
- EMERGENCY: Every 5 seconds (for critical situations)
- IDLE: Every 30 seconds (default)
- SLEEP: Every 15 minutes (for low-priority background agents)
Missing one interval triggers AGENT_UNHEALTHY (error code 480).
Response
{
"message": "Heartbeat recorded"
}
Submit Metrics
Agents send metrics separately from heartbeats. Metrics contain resource telemetry only.
Separation is Key: Metrics are sent on a separate channel from heartbeats (typically every 60 seconds). This ensures large telemetry payloads cannot starve the control path.
curl -X POST https://plugged.in/api/agents/AGENT_UUID/metrics \
-H "Authorization: Bearer YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"cpu_percent": 12.5,
"memory_mb": 128,
"requests_handled": 45,
"custom_metrics": {
"queue_depth": 3,
"cache_hit_rate": 0.85
}
}'
Request Body
| Field | Type | Required | Description |
|---|
cpu_percent | number | Yes | CPU usage percentage (0-100) |
memory_mb | number | Yes | Memory usage in megabytes |
requests_handled | integer | Yes | Total requests handled since start |
custom_metrics | object | No | Agent-specific custom metrics |
Response
{
"message": "Metrics recorded"
}
Common Patterns
Create and Wait for Activation
#!/bin/bash
# Create agent
RESPONSE=$(curl -s -X POST https://plugged.in/api/agents \
-H "Authorization: Bearer $API_KEY" \
-H "Content-Type: application/json" \
-d '{"name": "test-agent"}')
AGENT_UUID=$(echo $RESPONSE | jq -r '.agent.uuid')
echo "Created agent: $AGENT_UUID"
# Poll until ACTIVE
while true; do
STATE=$(curl -s https://plugged.in/api/agents/$AGENT_UUID \
-H "Authorization: Bearer $API_KEY" \
| jq -r '.agent.state')
echo "Current state: $STATE"
if [ "$STATE" = "ACTIVE" ]; then
echo "Agent is now ACTIVE!"
break
fi
sleep 5
done
List Healthy Agents
// Fetch all agents and filter by state
const response = await fetch('https://plugged.in/api/agents', {
headers: { 'Authorization': `Bearer ${API_KEY}` }
});
const agents = await response.json();
const healthy = agents.filter(agent =>
agent.state === 'ACTIVE' &&
agent.last_heartbeat_at &&
(Date.now() - new Date(agent.last_heartbeat_at) < 60000) // < 1 minute ago
);
console.log(`${healthy.length} healthy agents`);
Bulk Termination
import requests
# Get all agents
response = requests.get(
'https://plugged.in/api/agents',
headers={'Authorization': f'Bearer {API_KEY}'}
)
agents = response.json()
# Filter agents to terminate (e.g., by name pattern)
to_terminate = [a for a in agents if a['name'].startswith('test-')]
# Terminate each
for agent in to_terminate:
response = requests.delete(
f'https://plugged.in/api/agents/{agent["uuid"]}',
headers={'Authorization': f'Bearer {API_KEY}'}
)
print(f"Terminated {agent['name']}: {response.json()}")
Rate Limits
API endpoints are subject to rate limits to ensure fair usage:
| Tier | Requests per Minute | Burst |
|---|
| Free | 60 | 10 |
| Pro | 300 | 50 |
| Enterprise | 1000 | 100 |
Rate limit headers:
X-RateLimit-Limit: 60
X-RateLimit-Remaining: 45
X-RateLimit-Reset: 1699999999
429 Too Many Requests: If you exceed rate limits, wait for the reset time indicated in X-RateLimit-Reset header.
Webhook Events (Coming Soon)
Subscribe to agent lifecycle events via webhooks:
{
"event": "agent.activated",
"agent_uuid": "...",
"agent_name": "my-agent",
"timestamp": "2025-11-13T08:00:45Z",
"data": {
"from_state": "PROVISIONED",
"to_state": "ACTIVE"
}
}
Available Events:
agent.created
agent.provisioned
agent.activated
agent.draining
agent.terminated
agent.killed
agent.unhealthy
SDK Support
Official Plugged.in SDKs now include agent management:
SDK Examples
import { PluggedInClient } from 'pluggedinkit-js';
const client = new PluggedInClient({
apiKey: process.env.PLUGGEDIN_API_KEY
});
// Create an agent
const result = await client.agents.create({
name: 'my-agent',
description: 'My first PAP agent',
resources: {
cpu_request: '100m',
memory_request: '256Mi',
cpu_limit: '1000m',
memory_limit: '1Gi'
}
});
console.log(`Agent created: ${result.agent.uuid}`);
console.log(`DNS: ${result.agent.dns_name}`);
// List all agents
const agents = await client.agents.list();
// Get agent details
const details = await client.agents.get(agentId);
// Send heartbeat (from within agent)
await client.agents.heartbeat(agentId, {
mode: 'IDLE',
uptime_seconds: process.uptime()
});
// Send metrics (from within agent)
await client.agents.metrics(agentId, {
cpu_percent: 12.5,
memory_mb: 128,
requests_handled: 45
});
// Export agent data
const exportData = await client.agents.export(agentId, {
include_telemetry: true,
telemetry_limit: 1000
});
// Delete agent
await client.agents.delete(agentId);
from pluggedinkit import PluggedInClient
client = PluggedInClient(api_key=os.getenv('PLUGGEDIN_API_KEY'))
# Create an agent
result = client.agents.create({
'name': 'my-agent',
'description': 'My first PAP agent',
'resources': {
'cpu_request': '100m',
'memory_request': '256Mi',
'cpu_limit': '1000m',
'memory_limit': '1Gi'
}
})
print(f"Agent created: {result['agent']['uuid']}")
print(f"DNS: {result['agent']['dns_name']}")
# List all agents
agents = client.agents.list()
# Get agent details
details = client.agents.get(agent_id)
# Send heartbeat (from within agent)
client.agents.heartbeat(
agent_id,
mode='IDLE',
uptime_seconds=time.process_time()
)
# Send metrics (from within agent)
client.agents.metrics(
agent_id,
cpu_percent=12.5,
memory_mb=128,
requests_handled=45
)
# Export agent data
export_data = client.agents.export(
agent_id,
include_telemetry=True,
telemetry_limit=1000
)
# Delete agent
client.agents.delete(agent_id)
package main
import (
"context"
"fmt"
"os"
"github.com/pluggedin/pluggedinkit-go"
)
func main() {
client := pluggedinkit.NewClient(os.Getenv("PLUGGEDIN_API_KEY"))
ctx := context.Background()
// Create an agent
result, err := client.Agents.Create(ctx, pluggedinkit.CreateAgentRequest{
Name: "my-agent",
Description: strPtr("My first PAP agent"),
Resources: &pluggedinkit.ResourceRequirements{
CPURequest: strPtr("100m"),
MemoryRequest: strPtr("256Mi"),
CPULimit: strPtr("1000m"),
MemoryLimit: strPtr("1Gi"),
},
})
if err != nil {
panic(err)
}
fmt.Printf("Agent created: %s\n", result.Agent.UUID)
fmt.Printf("DNS: %s\n", result.Agent.DNSName)
// List all agents
agents, err := client.Agents.List(ctx)
// Get agent details
details, err := client.Agents.Get(ctx, agentID)
// Send heartbeat (from within agent)
_, err = client.Agents.Heartbeat(ctx, agentID, "IDLE", 330.0)
// Send metrics (from within agent)
_, err = client.Agents.Metrics(ctx, agentID, 12.5, 128, 45, nil)
// Export agent data
exportData, err := client.Agents.Export(ctx, agentID, true, 1000)
// Delete agent
_, err = client.Agents.Delete(ctx, agentID)
}
func strPtr(s string) *string { return &s }
Next Steps