> ## 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.

# Machine Markets: Orchestration

> Runtime endpoints, machine-side agents, graph, tasks, routes, outcomes, runs, policies, and audit events on the peaqOS Machine Markets API.

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 `Route`s, `Outcome`s, and `Run`s. Policies enforce cross-cutting rules. Audit events log every state change.

See the [Machine Markets overview](/peaqos/api-reference/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`

```ts theme={"theme":{"light":"github-light-default","dark":"github-dark"}}
type ListRuntimeEndpointsResponse = ListResponse<RuntimeEndpointView>;
```

### `GET /machines/:machineId/runtime-endpoints/:providerKey`

```ts theme={"theme":{"light":"github-light-default","dark":"github-dark"}}
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.

```ts theme={"theme":{"light":"github-light-default","dark":"github-dark"}}
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.

```ts theme={"theme":{"light":"github-light-default","dark":"github-dark"}}
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`.

```ts theme={"theme":{"light":"github-light-default","dark":"github-dark"}}
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.

```ts theme={"theme":{"light":"github-light-default","dark":"github-dark"}}
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`

```ts theme={"theme":{"light":"github-light-default","dark":"github-dark"}}
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`

```ts theme={"theme":{"light":"github-light-default","dark":"github-dark"}}
type GetGraphResponse = {
  nodes: GraphNodeRecord[];
  edges: GraphEdgeRecord[];
};
```

### `POST /machines/:machineId/graph/nodes`

```ts theme={"theme":{"light":"github-light-default","dark":"github-dark"}}
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`

```ts theme={"theme":{"light":"github-light-default","dark":"github-dark"}}
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

```ts theme={"theme":{"light":"github-light-default","dark":"github-dark"}}
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`

```ts theme={"theme":{"light":"github-light-default","dark":"github-dark"}}
type ListTasksResponse = ListResponse<TaskRecord>;
```

### `POST /machines/:machineId/tasks`

```ts theme={"theme":{"light":"github-light-default","dark":"github-dark"}}
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`

```ts theme={"theme":{"light":"github-light-default","dark":"github-dark"}}
type GetTaskResponse = ItemResponse<TaskRecord>;
```

### `POST /machines/:machineId/tasks/:taskId/discover`

Runs discovery against the catalogue for a queued task.

```ts theme={"theme":{"light":"github-light-default","dark":"github-dark"}}
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.

```ts theme={"theme":{"light":"github-light-default","dark":"github-dark"}}
type ResolveTaskResponse = {
  item: RouteRecord;
  resolution: TaskResolution;
};
```

### `POST /machines/:machineId/tasks/:taskId/execute`

Executes the resolved route. HTTP status propagates from the inner execution result.

```ts theme={"theme":{"light":"github-light-default","dark":"github-dark"}}
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

```ts theme={"theme":{"light":"github-light-default","dark":"github-dark"}}
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`

```ts theme={"theme":{"light":"github-light-default","dark":"github-dark"}}
type ListRoutesResponse = ListResponse<RouteRecord>;
```

### `GET /machines/:machineId/outcomes`

```ts theme={"theme":{"light":"github-light-default","dark":"github-dark"}}
type ListOutcomesResponse = ListResponse<OutcomeRecord>;
```

### `GET /runs?machineId={id}&...`

```ts theme={"theme":{"light":"github-light-default","dark":"github-dark"}}
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`

```ts theme={"theme":{"light":"github-light-default","dark":"github-dark"}}
type GetRunResponse = {
  run: RunRecord;
  outcome: OutcomeRecord | null;
};
```

### Route / Outcome / Run types

```ts theme={"theme":{"light":"github-light-default","dark":"github-dark"}}
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 <Tooltip tip={G.pairing.def}>pairing</Tooltip>'s <Tooltip tip={G.delegationPolicy.def}>delegation policy</Tooltip>.

### `GET /policies`

```ts theme={"theme":{"light":"github-light-default","dark":"github-dark"}}
type ListPoliciesResponse = ListResponse<PolicyRecord>;
```

### `POST /policies`

```ts theme={"theme":{"light":"github-light-default","dark":"github-dark"}}
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`

```ts theme={"theme":{"light":"github-light-default","dark":"github-dark"}}
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`

```ts theme={"theme":{"light":"github-light-default","dark":"github-dark"}}
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`.

## Related

* [Machine Markets overview](/peaqos/api-reference/machine-markets-overview)
* [Machine Markets: Orders & payments](/peaqos/api-reference/machine-markets-orders)
* [Machine Markets: Pairings](/peaqos/api-reference/machine-markets-pairings)
* [Machine Markets: Skills, services, search](/peaqos/api-reference/machine-markets-discovery)
* [Machine Markets concept](/peaqos/concepts/machine-markets)
* [Scale function](/peaqos/functions/scale)
