Documentation Index
Fetch the complete documentation index at: https://docs.peaq.xyz/llms.txt
Use this file to discover all available pages before exploring further.
The orchestration surface is what makes a service execute end to end. Native skills run through registered runtime endpoints. Machine-side runtime agents register and heartbeat their own endpoints. Tasks model a discover-resolve-execute lifecycle and emit Routes, Outcomes, and Runs. Policies enforce cross-cutting rules. Audit events log every state change.
See the Machine Markets overview for base path, auth model, and common envelopes.
All endpoints below require the machine to be active and identity-verified. Errors MACHINE_NOT_ACTIVE and MACHINE_NOT_ACTIVATED are uniform across the group.
Runtime endpoints
GET /machines/:machineId/runtime-endpoints
type ListRuntimeEndpointsResponse = ListResponse<RuntimeEndpointView>;
GET /machines/:machineId/runtime-endpoints/:providerKey
type GetRuntimeEndpointResponse = ItemResponse<RuntimeEndpointView>;
PUT /machines/:machineId/runtime-endpoints/:providerKey
Upsert a manual runtime endpoint. source is forced to manual. Returns 201 on create, 200 on update. The server lowercases providerKey in the path.
type UpsertRuntimeEndpointRequest = {
endpointBaseUrl: string; // http/https URL, path/query/hash stripped
label?: string; // default `${providerKey.toUpperCase()} runtime`
authType?: "none" | "api-key" | "bearer";
authToken?: string;
modelAlias?: string;
networkScope?: "loopback" | "lan" | "vpn" | "public";
probePath?: string; // default "/v1/models" for qvac, else "/health"
status?: "active" | "degraded" | "unreachable";
leaseExpiresAt?: string; // ISO 8601
};
type UpsertRuntimeEndpointResponse = ItemResponse<RuntimeEndpointView>;
Errors: VALIDATION_ERROR (invalid URL).
DELETE /machines/:machineId/runtime-endpoints/:providerKey
Returns 204. Errors: NOT_FOUND.
RuntimeEndpointView
The view never echoes authToken. Use hasAuthToken to check whether one is stored.
type RuntimeEndpointView = {
id: string;
machineId: string;
providerKey: string;
source: "manual" | "agent";
agentId: string | null;
label: string;
endpointBaseUrl: string;
authType: "none" | "api-key" | "bearer";
hasAuthToken: boolean;
modelAlias: string | null;
networkScope: "loopback" | "lan" | "vpn" | "public";
probePath: string;
status: "active" | "degraded" | "unreachable";
leaseExpiresAt: string | null;
lastCheckedAt: string | null;
lastHealthyAt: string | null;
createdAt: string;
updatedAt: string;
};
Machine-side agents
POST /machines/:machineId/agents/enrollment
Enrols a new runtime agent. Returns the agent record plus a one-time agentToken.
type EnrollMachineAgentRequest = {
label?: string; // default "Machine runtime agent"
allowedProviderKeys?: string[]; // default ["qvac"], normalised to lowercase
providerKeys?: string[]; // alias for allowedProviderKeys
};
type EnrollMachineAgentResponse = {
item: MachineAgentView & { agentToken: string };
};
POST /machine-agents/heartbeat
Heartbeat from an on-machine runtime agent. Auth via agentToken body field or x-agent-pairing-token / Authorization: Bearer header. Upserts one or many endpoints with source: "agent" and a lease.
type HeartbeatRequest = {
machineId: string;
agentId: string;
agentToken?: string;
leaseSeconds?: number; // [30, 86400], default 120
endpoints?: Array<{
providerKey: string;
endpointBaseUrl: string;
label?: string;
authType?: "none" | "api-key" | "bearer";
authToken?: string;
modelAlias?: string;
networkScope?: "loopback" | "lan" | "vpn" | "public";
probePath?: string;
status?: "active" | "degraded" | "unreachable";
}>;
};
type HeartbeatResponse = {
item: MachineAgentView;
endpoints: RuntimeEndpointView[];
};
Errors: VALIDATION_ERROR, NOT_FOUND, AGENT_PAIRING_INACTIVE (status revoked), AGENT_AUTH_REQUIRED, AGENT_AUTH_INVALID, AGENT_POLICY_DENIED (provider key not in agent.allowedProviderKeys).
Side effects: agent status flips to active, lastSeenAt updates, each endpoint upserts with leaseExpiresAt = now + leaseSeconds.
MachineAgentView
type MachineAgentView = {
id: string;
machineId: string;
label: string;
allowedProviderKeys: string[];
status: "provisioned" | "active" | "revoked";
hasAgentToken: boolean;
lastSeenAt: string | null;
createdAt: string;
updatedAt: string;
};
Graph
GET /machines/:machineId/graph
type GetGraphResponse = {
nodes: GraphNodeRecord[];
edges: GraphEdgeRecord[];
};
POST /machines/:machineId/graph/nodes
type CreateGraphNodeRequest = {
type: NodeType;
title: string;
refId?: string;
position?: { x?: number; y?: number };
data?: Record<string, unknown>;
};
type CreateGraphNodeResponse = ItemResponse<GraphNodeRecord>;
PATCH /machines/:machineId/graph/nodes/:nodeId
All fields optional; data is shallow-merged. Status accepted: "draft" | "active" | "blocked" | "degraded".
DELETE /machines/:machineId/graph/nodes/:nodeId
Returns 204.
POST /machines/:machineId/graph/edges
type CreateGraphEdgeRequest = {
sourceNodeId: string;
targetNodeId: string;
type: EdgeType;
label?: string;
data?: Record<string, unknown>;
};
type CreateGraphEdgeResponse = ItemResponse<GraphEdgeRecord>;
PATCH /machines/:machineId/graph/edges/:edgeId / DELETE /machines/:machineId/graph/edges/:edgeId
Standard patch and delete semantics.
Graph types
type NodeType =
| "machine" | "trigger" | "skill" | "task" | "policy" | "discovery"
| "storage" | "memory" | "handoff" | "route" | "outcome" | "catalog-entry";
type EdgeType =
| "connects" | "request" | "discover" | "constrain" | "candidate"
| "route" | "result" | "route-select" | "route-source" | "execute"
| "handoff" | "receipt" | "sync" | "allow" | "deny";
type GraphNodeRecord = {
id: string;
machineId: string;
type: NodeType;
refId?: string | null;
title: string;
status: "draft" | "active" | "blocked" | "degraded";
position: { x: number; y: number };
data: Record<string, unknown>;
createdAt: string;
updatedAt: string;
};
type GraphEdgeRecord = {
id: string;
machineId: string;
sourceNodeId: string;
targetNodeId: string;
type: EdgeType;
label?: string;
data: Record<string, unknown>;
createdAt: string;
updatedAt: string;
};
Tasks
Tasks model the discover-resolve-execute pipeline that orders ride on. POST /market/orders materialises an order as a task internally, but tasks can also be driven directly.
GET /machines/:machineId/tasks
type ListTasksResponse = ListResponse<TaskRecord>;
POST /machines/:machineId/tasks
type CreateTaskRequest = {
serviceType: ServiceType;
taskType: string;
operation?: string;
input?: Record<string, unknown>;
constraints?: {
region?: string;
budget?: number; // >= 0
allowExternalHandoff?: boolean; // default true
requireNativeExecution?: boolean;// default false
requiredCapabilities?: string[];
preferredSkillKeys?: string[]; // must all be known skills
};
discovery?: { maxCandidates?: number }; // 1..25, default 10
};
type CreateTaskResponse = ItemResponse<TaskRecord>;
GET /machines/:machineId/tasks/:taskId
type GetTaskResponse = ItemResponse<TaskRecord>;
POST /machines/:machineId/tasks/:taskId/discover
Runs discovery against the catalogue for a queued task.
type DiscoverTaskRequest = {
allowExternalHandoff?: boolean;
requireNativeExecution?: boolean;
maxCandidates?: number;
};
type DiscoverTaskResponse = {
taskId: string;
machineId: string;
resolution: TaskResolution;
candidates: Candidate[];
};
POST /machines/:machineId/tasks/:taskId/resolve
Picks a route and persists it. Returns 201.
type ResolveTaskResponse = {
item: RouteRecord;
resolution: TaskResolution;
};
POST /machines/:machineId/tasks/:taskId/execute
Executes the resolved route. HTTP status propagates from the inner execution result.
type ExecuteTaskRequest = {
input?: Record<string, unknown>;
};
type ExecuteTaskResponse = {
item: OutcomeRecord;
run: RunRecord;
resolution: TaskResolution;
};
Errors: ROUTE_REQUIRED (no route resolved), plus all downstream skill-runtime errors.
Task types
type ExternalHandoff = {
label: string;
url: string;
notes: string[];
};
type Candidate = {
service: CanonicalService; // catalogue service shape, see machine-markets-discovery
score: number;
reasons: string[];
};
type TaskResolution =
| { kind: "native"; candidate: Candidate }
| { kind: "handoff"; candidate: Candidate; handoff: ExternalHandoff }
| { kind: "unavailable"; reason: string; candidates: Candidate[] };
type TaskStatus =
| "draft" | "queued" | "resolving" | "resolved"
| "executing" | "completed" | "failed" | "handed_off";
type TaskRecord = {
id: string;
machineId: string;
serviceType: ServiceType;
operation?: string;
taskType: string;
status: TaskStatus;
input: Record<string, unknown>;
constraints: {
region?: string;
budget?: number;
allowExternalHandoff: boolean;
requireNativeExecution: boolean;
requiredCapabilities: string[];
preferredSkillKeys?: string[];
};
discovery: { maxCandidates: number };
routeId?: string | null;
outcomeId?: string | null;
createdAt: string;
updatedAt: string;
};
Routes, outcomes, runs
GET /machines/:machineId/routes
type ListRoutesResponse = ListResponse<RouteRecord>;
GET /machines/:machineId/outcomes
type ListOutcomesResponse = ListResponse<OutcomeRecord>;
GET /runs?machineId={id}&...
type ListRunsQuery = {
machineId: string;
status?: RunStatus;
skillKey?: string;
serviceId?: string;
executionMode?: ExecutionMode;
limit?: number; // default 100, max 500
};
type ListRunsResponse = ListResponse<RunRecord>;
GET /runs/:runId
type GetRunResponse = {
run: RunRecord;
outcome: OutcomeRecord | null;
};
Route / Outcome / Run types
type RouteStatus = "selected" | "blocked" | "handed_off" | "failed" | "completed";
type OutcomeStatus = "pending" | "running" | "completed" | "failed" | "external";
type RunStatus = "queued" | "running" | "completed" | "failed" | "external" | "blocked";
type RouteRecord = {
id: string;
machineId: string;
taskId: string;
selectedSkillKey: string;
selectedServiceId: string;
executionMode: ExecutionMode;
integrationStatus: IntegrationStatus;
status: RouteStatus;
reason: string;
score: number;
fallbackChain: Array<{ skillKey: string; serviceId: string; executionMode: ExecutionMode }>;
handoff: ExternalHandoff | null;
createdAt: string;
updatedAt: string;
};
type OutcomeRecord = {
id: string;
machineId: string;
taskId: string;
routeId: string;
status: OutcomeStatus;
skillKey: string;
serviceId: string;
executionMode: ExecutionMode;
receiptRef?: string | null;
logsRef?: string | null;
externalRef?: string | null;
errorCode?: string | null;
errorMessage?: string | null;
result?: unknown;
createdAt: string;
updatedAt: string;
};
type RunRecord = {
id: string;
machineId: string;
taskId: string;
routeId?: string | null;
outcomeId?: string | null;
status: RunStatus;
executionMode?: ExecutionMode | null;
skillKey?: string | null;
serviceId?: string | null;
summary?: string | null;
createdAt: string;
updatedAt: string;
};
Policies
Policies enforce cross-cutting rules above and beyond a pairing’s delegation policy.
GET /policies
type ListPoliciesResponse = ListResponse<PolicyRecord>;
POST /policies
type CreatePolicyRequest = {
name: string;
allowedSkillKeys?: string[];
deniedSkillKeys?: string[];
budgetCap?: { amount: number; currency: string } | null;
regionRules?: string[];
nativeOnly?: boolean;
handoffAllowed?: boolean;
requiredCapabilities?: string[];
};
type CreatePolicyResponse = ItemResponse<PolicyRecord>;
GET /policies/:policyId / PATCH /policies/:policyId
Standard get and patch semantics.
PolicyRecord
type PolicyRecord = {
id: string;
name: string;
allowedSkillKeys: string[];
deniedSkillKeys: string[];
budgetCap?: { currency: string; amount: number } | null;
regionRules: string[];
nativeOnly: boolean;
handoffAllowed: boolean;
requiredCapabilities: string[];
createdAt: string;
updatedAt: string;
};
Audit events
GET /audit-events
type ListAuditEventsQuery = {
type?: string;
resourceType?: string;
resourceId?: string;
limit?: number; // default 100, max 500
};
type ListAuditEventsResponse = ListResponse<AuditEventRecord>;
type AuditEventRecord = {
id: string;
actor: string; // "api" | "system:machine-markets"
type: string;
resourceType: string;
resourceId: string;
payload: Record<string, unknown>;
createdAt: string;
};
Event types emitted by the orchestration surface: runtime-endpoint.upserted, runtime-endpoint.deleted, machine-agent.enrolled, machine-agent.heartbeat, graph.node.created, graph.node.updated, graph.node.deleted, graph.edge.created, graph.edge.updated, graph.edge.deleted, task.created, policy.created, policy.updated, market.order.created, market.payment.intent-created, market.payment.proof-recorded, market.order.executed, market.order.confirmed, market.order.disputed, market.payment.escrow-held, market.payment.released, market.payment.refunded.