Skip to main content

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 JS/TS SDK exposes the Machine Markets API as a typed namespace on the existing PeaqosClient. Same client, additive surface. The flat method layout matches the HTTP API one-to-one.

Setup

import 'dotenv/config';
import { PeaqosClient } from "@peaqos/peaq-os-sdk";

const client = PeaqosClient.fromEnv();
const { items } = await client.orchestration.listMachines({ limit: 20 });
Touching client.orchestration without orchestrationUrl configured throws OrchestrationConfigError. The namespace lazy-initialises and caches per client.

Configuration

FieldEnv var (via fromEnv())Notes
orchestrationUrlPEAQOS_ORCHESTRATION_URLRequired to use the namespace. Must parse as http: or https:. Non-loopback http:// triggers a warning.
apiKeyPEAQOS_API_KEYRequired for platform-auth calls. Sent as x-api-key.
Constructor form when not using env:
new PeaqosClient({
  rpcUrl,
  privateKey,
  contracts,
  orchestrationUrl: "https://your-orchestrator-host",
  apiKey: "...",
});
Transport constants:
  • API_BASE_PATH = "/api/v1", prepended to every path.
  • Timeouts: 30 s GET, 60 s POST/PATCH/PUT/DELETE.
  • User-Agent: peaq-os-sdk-js/<version>.
No retry, no exponential backoff. Six auto-paginated iterators are available; see Pagination below.

Common envelopes

type ListResponse<T> = {
  readonly items: readonly T[];
  readonly nextCursor?: string;          // omitted when there is no next page
};

type ItemResponse<T> = {
  readonly item: Readonly<T>;
};

type ListQuery = {
  readonly limit?: number;               // 1..500, default 100
  readonly cursor?: string;              // opaque, omit on first page
};
204 No Content resolves to void. validateListQuery rejects limit < 1 or limit > 500 with OrchestrationValidationError before the HTTP call.

Machines

client.orchestration.listMachines(options?: {
  cursor?: string;
  limit?: number;
}): Promise<ListResponse<Machine>>

client.orchestration.createMachine(params: CreateMachineRequest)
  : Promise<ItemResponse<Machine>>

client.orchestration.getMachine(machineId: string)
  : Promise<ItemResponse<Machine>>

client.orchestration.updateMachine(machineId: string, params: UpdateMachineRequest)
  : Promise<ItemResponse<Machine>>

client.orchestration.archiveMachine(machineId: string): Promise<void>
Machine shape:
type Machine = {
  readonly id: string;
  readonly displayName: string;
  readonly status: "draft" | "active" | "degraded" | "blocked" | "archived";
  readonly ownerId: string;
  readonly identityRef?: string | null;
  readonly identityProof?: {
    readonly method: "eip191";
    readonly identityRef: string;
    readonly signerAddress: string;
    readonly challengeId: string;
    readonly verifiedAt: string;
    readonly challengeExpiresAt: string;
    readonly controllerAddresses: readonly string[];
    readonly resolutionSource: "peaqos-mcr";
  } | null;
  readonly machineType: string;
  readonly runtimeProfile: string;
  readonly capabilities: readonly string[];
  readonly labels: Readonly<Record<string, string>>;
  readonly policyIds: readonly string[];
  readonly skillKeys: readonly string[];
  readonly createdAt: string;
  readonly updatedAt: string;
};

Machine identity challenges

client.orchestration.createMachineIdentityChallenge(
  params: CreateMachineIdentityChallengeRequest,
): Promise<ItemResponse<MachineIdentityChallengeResponse>>
Requests a server-issued challenge tied to a did:peaq:0x... or peaqos:machine:<id> identity reference. The DID controller signs item.message with EIP-191 personal_sign and submits the resulting { challengeId, signature } as identityProof when creating or updating the machine record.

Agent pairings

client.orchestration.listAgentPairings(machineId: string, options?: ListAgentPairingsOptions)
  : Promise<ListResponse<AgentPairing>>

client.orchestration.createAgentPairingChallenge(
  machineId: string,
  params: CreateAgentPairingChallengeRequest,
): Promise<ItemResponse<AgentPairingChallengeResponse>>

client.orchestration.createAgentPairing(
  machineId: string,
  params: CreateAgentPairingRequest,
): Promise<ItemResponse<AgentPairing & { pairingToken: string }>>

client.orchestration.createAgentPairingSession(
  machineId: string,
  pairingId: string,
  params: CreateAgentPairingSessionRequest,
): Promise<ItemResponse<AgentPairing & { pairingToken: string }>>

client.orchestration.updateAgentPairing(
  machineId: string,
  pairingId: string,
  params: UpdateAgentPairingRequest,
): Promise<ItemResponse<AgentPairing>>

client.orchestration.revokeAgentPairing(machineId: string, pairingId: string)
  : Promise<void>
Pairing is challenge-based end to end:
  1. createAgentPairingChallenge returns a server-issued challenge keyed to agentAddress, agentProvider, agentRole, and optional agentDid.
  2. The Machine Agent signs item.message (EIP-191) with the wallet key behind agentAddress.
  3. createAgentPairing accepts the proof in params.agentProof and returns the pairing with a signed HS256 session JWT in pairingToken. The token is returned once at create.
  4. createAgentPairingSession rotates the session token before expiry with a fresh proof. Required after any updateAgentPairing to the delegation policy, since policy changes invalidate the current token’s delegationPolicyHash.
createAgentPairing and createAgentPairingSession return the pairingToken exactly once each. toJSON redacts the token to "[REDACTED]" so it does not leak through JSON.stringify. CreateAgentPairingRequest now requires agentProof: { challengeId, signature } and accepts optional agentDid. delegationPolicy.allowedServiceIds and delegationPolicy.deniedServiceIds join the existing skill-level allow/deny lists.

Machine agents

client.orchestration.listMachineAgents(machineId: string)
  : Promise<ListResponse<MachineAgent>>

client.orchestration.enrollMachineAgent(
  machineId: string,
  params?: EnrollMachineAgentRequest,
): Promise<ItemResponse<MachineAgent & { provisioningToken: string }>>

client.orchestration.revokeMachineAgent(machineId: string, agentId: string)
  : Promise<void>

client.orchestration.machineAgentHeartbeat(
  params: MachineAgentHeartbeatRequest,
): Promise<MachineAgentHeartbeatResponse>
enrollMachineAgent POSTs /machines/:machineId/agents/enrollment and returns a one-time provisioningToken. machineAgentHeartbeat uses no auth header — the agentToken rides in the request body.

Runtime endpoints

client.orchestration.listRuntimeEndpoints(machineId: string, options?: ListRuntimeEndpointsOptions)
  : Promise<ListResponse<RuntimeEndpoint>>

client.orchestration.getRuntimeEndpoint(machineId: string, providerKey: string)
  : Promise<ItemResponse<RuntimeEndpoint>>

client.orchestration.upsertRuntimeEndpoint(
  machineId: string,
  providerKey: string,
  params: UpsertRuntimeEndpointRequest,
): Promise<ItemResponse<RuntimeEndpoint>>

client.orchestration.deleteRuntimeEndpoint(machineId: string, providerKey: string)
  : Promise<void>
Upsert uses HTTP PUT. endpointBaseUrl is parsed with new URL(...) client-side; invalid URLs throw OrchestrationValidationError(field: "endpointBaseUrl", constraint: "valid URL").

Skills

client.orchestration.listSkills(options?: {
  scope?: SkillScope;
  direction?: SkillDirection;
  source?: SkillSource;
}): Promise<ListResponse<SkillSummary>>

client.orchestration.getSkill(skillKey: string)
  : Promise<ItemResponse<SkillSummary>>

client.orchestration.getSkillManifest(skillKey: string)
  : Promise<ItemResponse<SkillManifest>>

client.orchestration.updateSkillConfig(
  skillKey: string,
  params: UpdateSkillConfigRequest,
): Promise<ItemResponse<SkillConfigResponse>>

Market services

client.orchestration.listMarketServices(options?: {
  machineId?: string;
  serviceType?: ServiceType;
  executionMode?: ExecutionMode;
  providerKey?: string;
}): Promise<ListResponse<MarketService>>

client.orchestration.getMarketService(
  serviceId: string,
  options?: { machineId?: string },
): Promise<ItemResponse<MarketService>>
client.orchestration.searchMarket(
  params: MarketSearchRequest,
  pairingToken: string,
): Promise<ItemResponse<MarketSearch>>

client.orchestration.getMarketSearch(searchId: string)
  : Promise<ItemResponse<MarketSearch>>
searchMarket uses x-agent-pairing-token auth — the token rides in the second argument and is sent per call. getMarketSearch falls back to platform auth.

Market orders

client.orchestration.listMarketOrders(
  options: ListMarketOrdersOptions,        // { machineId, cursor?, limit? }
): Promise<ListResponse<MarketOrder>>

client.orchestration.createMarketOrder(
  params: CreateMarketOrderRequest,        // { machineId, agentPairingId, serviceId, searchId?, quoteId?, operation?, input?, budget?, providerCredentials? }
  pairingToken: string,
): Promise<ItemResponse<MarketOrder>>

client.orchestration.getMarketOrder(
  orderId: string,
): Promise<ItemResponse<MarketOrder>>

client.orchestration.executeMarketOrder(
  orderId: string,
  params: ExecuteMarketOrderRequest,       // { input? }
  pairingToken: string,
): Promise<ExecuteMarketOrderResponse>     // discriminated union

client.orchestration.confirmMarketOrder(
  orderId: string,
  pairingToken: string,
): Promise<ConfirmMarketOrderResponse>     // { order, payment: MarketPayment | null }

client.orchestration.disputeMarketOrder(
  orderId: string,
  params: DisputeMarketOrderRequest,       // { reason, evidence? }
  pairingToken: string,
): Promise<DisputeMarketOrderResponse>     // { order, payment, dispute }; payment → "frozen"
listMarketOrders and getMarketOrder use platform auth; the four mutating calls require the agent’s pairingToken. executeMarketOrder returns a discriminated union on execution.statusCode:
const result = await client.orchestration.executeMarketOrder(orderId, {}, pairingToken);
if (result.execution.statusCode === 200) {
  // Native execution
  const { run, outcome } = result.execution;
} else {
  // 202 — external handoff
  const { handoff } = result.execution;   // { label, url, notes }
}

Payment settlement

client.orchestration.createPaymentIntent(
  orderId: string,
  params: CreatePaymentIntentRequest,      // { rail?, amount?, currency? }
  pairingToken: string,
): Promise<ItemResponse<MarketPayment>>

client.orchestration.getMarketPayment(
  orderId: string,
): Promise<ItemResponse<MarketPayment | null>>  // platform auth; item may be null

client.orchestration.submitPaymentProof(
  orderId: string,
  params: SubmitPaymentProofRequest,       // EVM: transactionHash | Solana: transactionSignature + verificationMode + chain, token, payerAddress, payeeAddress, amount
  pairingToken: string,
): Promise<ItemResponse<MarketPayment>>

client.orchestration.lockEscrowPayment(
  orderId: string,
  params: LockEscrowPaymentRequest,        // { transactionHash, chain, escrowAddress } all required
  pairingToken: string,
): Promise<ItemResponse<MarketPayment>>

client.orchestration.releasePayment(
  orderId: string,
  params: ReleasePaymentRequest | undefined,  // { transactionHash?, notes? }
  pairingToken: string,
): Promise<ItemResponse<MarketPayment>>

client.orchestration.refundPayment(
  orderId: string,
  params: RefundPaymentRequest | undefined,   // { transactionHash?, reason? }
  pairingToken: string,
): Promise<ItemResponse<MarketPayment>>
submitPaymentProof accepts EVM (transactionHash) and Solana (transactionSignature) shapes with verificationMode: "recorded" | "rpc". RPC verification cross-checks the ERC-20 Transfer log against expected token, payerAddress, payeeAddress, and amount.

Pagination

Six auto-paginated iterators ship in this release:
for await (const m of client.orchestration.listMachinesAll()) { /* … */ }
for await (const s of client.orchestration.listSkillsAll()) { /* … */ }
for await (const svc of client.orchestration.listMarketServicesAll()) { /* … */ }
for await (const o of client.orchestration.listMarketOrdersAll({ machineId: "m-1" })) { /* … */ }
for await (const p of client.orchestration.listAgentPairingsAll("m-1")) { /* … */ }
for await (const e of client.orchestration.listRuntimeEndpointsAll("m-1")) { /* … */ }
Each *All accepts Omit<XxxOptions, "cursor"> — cursor is managed internally; filters and limit pass through on every page. For manual pagination, the underlying list* methods now accept cursor and limit:
let cursor: string | undefined;
do {
  const page = await client.orchestration.listMarketOrders({ machineId: "m-1", limit: 50, cursor });
  for (const order of page.items) handle(order);
  cursor = page.nextCursor;             // undefined when terminal
} while (cursor);

Coming next

These endpoints ship on the HTTP API (see Machine Markets API: Orchestration). SDK method bindings follow shortly:
  • Tasks: createTask, discoverTask, resolveTask, executeTask, getTask, listTasks
  • Graph: getMachineGraph, createGraphNode, updateGraphNode, deleteGraphNode, createGraphEdge, updateGraphEdge, deleteGraphEdge
  • Policies: listPolicies, createPolicy, getPolicy, updatePolicy
  • Observability: listAuditEvents, listRuns, getRun, getReadiness, getHealth
  • Order family extras: cancelMarketOrder, retryMarketOrder
Types for policies, observability, and the remaining order-family operations already ship in @peaqos/peaq-os-sdk so application code can prepare for the methods landing.

Errors

Five exported classes, all extend PeaqosError:
class OrchestrationError extends PeaqosError {}

class OrchestrationApiError extends OrchestrationError {
  readonly code: string;        // server-provided, e.g. "NOT_FOUND"
  readonly statusCode: number;  // HTTP 4xx/5xx
  readonly details: unknown;    // server-provided detail blob
}

class OrchestrationNetworkError extends OrchestrationError {
  // .cause carries the original transport error
}

class OrchestrationConfigError extends OrchestrationError {}

class OrchestrationValidationError extends OrchestrationError {
  readonly field: string;
  readonly constraint: string;
}
Switch on instanceof OrchestrationApiError then on err.code. Never parse err.message. Server code values exported as constants from @peaqos/peaq-os-sdk: ERROR_CODE_AUTH_REQUIRED, ERROR_CODE_AUTH_INVALID, ERROR_CODE_AGENT_AUTH_REQUIRED, ERROR_CODE_AGENT_AUTH_INVALID, ERROR_CODE_AGENT_AUTH_EXPIRED, ERROR_CODE_VALIDATION_ERROR, ERROR_CODE_NOT_FOUND, ERROR_CODE_MACHINE_NOT_ACTIVE, ERROR_CODE_MACHINE_NOT_ACTIVATED, ERROR_CODE_MACHINE_IDENTITY_EXISTS, ERROR_CODE_MACHINE_IDENTITY_IMMUTABLE, ERROR_CODE_MACHINE_IDENTITY_PROOF_REQUIRED, ERROR_CODE_MACHINE_IDENTITY_PROOF_INVALID, ERROR_CODE_MACHINE_IDENTITY_PROOF_EXPIRED, ERROR_CODE_PEAQOS_IDENTITY_UNAVAILABLE, ERROR_CODE_AGENT_PAIRING_PROOF_REQUIRED, ERROR_CODE_AGENT_PAIRING_PROOF_INVALID, ERROR_CODE_AGENT_PAIRING_PROOF_EXPIRED, ERROR_CODE_AGENT_PAIRING_UNAVAILABLE, ERROR_CODE_AGENT_PAIRING_REQUIRED, ERROR_CODE_AGENT_PAIRING_INACTIVE, ERROR_CODE_AGENT_POLICY_DENIED, ERROR_CODE_AGENT_SPEND_LIMIT_EXCEEDED, ERROR_CODE_AGENT_DAILY_LIMIT_EXCEEDED, ERROR_CODE_QUOTE_EXPIRED, ERROR_CODE_ORDER_CLOSED, ERROR_CODE_ORDER_NOT_DELIVERED, ERROR_CODE_PAYMENT_REQUIRED, ERROR_CODE_PAYMENT_RPC_REQUIRED, ERROR_CODE_PAYMENT_RPC_ERROR, ERROR_CODE_PAYMENT_TX_FAILED, ERROR_CODE_EXECUTION_UNSUPPORTED, ERROR_CODE_ENDPOINT_UNREACHABLE. New types now exported (shipping with the same dev-orch release as the methods above):
type MachineIdentityProofInput = { challengeId: string; signature: string };

type AgentPairingChallengeResponse = {
  challengeId: string;
  agentAddress: string;
  message: string;
  expiresAt: string;
  verificationMethod: "eip191";
};

type AgentPairingProof = { challengeId: string; signature: string };

type ProviderCredentials = {
  // Per-adapter credentials keyed by providerKey. Shape is
  // adapter-specific and documented per-adapter on robotic.sh.
  // Always redacted before request bodies are persisted.
  [providerKey: string]: Record<string, string | undefined>;
};
MarketPaymentRailType adds "wdk-usdt-transfer" alongside the existing "x402" | "mpp" | "wallet" | "vault-stripe" | "escrow" | "onchain-escrow" | "offchain-record" | "external" | "not-required" set. The transport synthesises BAD_RESPONSE (non-JSON error body) and INVALID_RESPONSE_SHAPE (envelope mismatch) inside OrchestrationApiError when the server response is malformed.

Credential handling

Three auth modes per call:
  • Platform (x-api-key) — set once on the client via apiKey, sent on every call by default.
  • Agent pairing (x-agent-pairing-token) — passed per call to searchMarket as the second argument. The SDK does not store or rotate it.
  • None — used only by machineAgentHeartbeat (token rides in the body).
sanitizeBody() redacts any body field whose name contains token, key, secret, password, credential, or auth (case-insensitive) before attaching the body to an error. Redaction is recursive into nested objects.