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
| Field | Env var (via fromEnv()) | Notes |
|---|
orchestrationUrl | PEAQOS_ORCHESTRATION_URL | Required to use the namespace. Must parse as http: or https:. Non-loopback http:// triggers a warning. |
apiKey | PEAQOS_API_KEY | Required 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:
createAgentPairingChallenge returns a server-issued challenge keyed to agentAddress, agentProvider, agentRole, and optional agentDid.
- The Machine Agent signs
item.message (EIP-191) with the wallet key behind agentAddress.
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.
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>>
Market search
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.
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.