> ## 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: Machine Agent pairings

> Challenge, pair, rotate, repolicy, and revoke Machine Agents on the peaqOS Machine Markets API.

A <Tooltip tip={G.machineAgent.def}>Machine Agent</Tooltip> <Tooltip tip={G.pairing.def}>pairing</Tooltip> binds a third-party AI agent (Claude, OpenAI, Virtuals, Teneo, or your own) to an activated, <Tooltip tip={G.bond.def}>bonded</Tooltip> peaqOS machine and attaches a <Tooltip tip={G.delegationPolicy.def}>delegation policy</Tooltip> that bounds what the agent can do on the machine's behalf. Pairing is challenge-based: peaqOS issues a machine-bound <Tooltip tip={G.challenge.def}>challenge</Tooltip>, the agent signs it with the wallet key behind `agentAddress` (<Tooltip tip={G.eip191.def}>EIP-191</Tooltip> `personal_sign`), and the orchestrator verifies the signature before persisting the pairing and issuing a signed session token.

See the [Machine Markets overview](/peaqos/api-reference/machine-markets-overview) for base path, auth model, and common envelopes.

## `AgentPairing` type

```ts theme={"theme":{"light":"github-light-default","dark":"github-dark"}}
type AgentPairing = {
  id: string;
  machineId: string;
  status: "active" | "paused" | "revoked";
  agentAddress: string;
  agentDid: string | null;       // did:pkh:eip155:1:<addr> when present
  agentProvider: string;         // "anthropic" | "openai" | "virtuals" | ...
  agentRole: string;             // free-form, e.g. "ops" | "trader" | "buyer"
  description?: string | null;
  verification: {
    method: "eip191";
    signerAddress: string;       // recovered signer; must match agentAddress
    verifiedAt: string;
    challengeExpiresAt: string;
  } | null;
  delegationPolicy: {
    allowedSkillKeys: string[];
    deniedSkillKeys: string[];
    allowedServiceIds: string[];
    deniedServiceIds: string[];
    perTransactionLimit?: number | null;
    dailySpendLimit?: number | null;
    currency?: string | null;
  };
  hasAuthToken: boolean;
  tokenLastFour: string | null;
  sessionId: string | null;
  sessionTokenId: string | null;
  sessionIssuedAt: string | null;
  sessionExpiresAt: string | null;
  pairingToken?: string;         // signed HS256 session JWT, returned once at create/rotate
  createdAt: string;
  updatedAt: string;
};
```

## `AgentPairingChallenge` and `AgentPairingProof`

```ts theme={"theme":{"light":"github-light-default","dark":"github-dark"}}
type AgentPairingChallenge = {
  challengeId: string;
  machineId: string;
  machineIdentityRef: string | null;
  agentAddress: string;
  agentDid: string;
  agentProvider: string;
  agentRole: string;
  message: string;            // sign this with the key behind agentAddress
  expiresAt: string;
  verificationMethod: "eip191";
};

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

## Endpoints

### `GET /machines/:machineId/agent-pairings`

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

### `POST /machines/:machineId/agent-pairings/challenges`

Returns a fresh challenge for an agent. The Machine Agent signs `item.message` (EIP-191) with the wallet key behind `agentAddress`, then submits the signature to the create-pairing endpoint or the session-rotation endpoint.

```ts theme={"theme":{"light":"github-light-default","dark":"github-dark"}}
type CreateAgentPairingChallengeRequest = {
  agentAddress: string;
  agentDid?: string;
  agentProvider: string;
  agentRole: string;
};

type CreateAgentPairingChallengeResponse = ItemResponse<AgentPairingChallenge>;
```

If `agentDid` is omitted, the orchestrator derives `did:pkh:eip155:1:<agentAddress>` from the normalised `agentAddress`. If supplied, `agentDid` must be of the form `did:pkh:eip155:<chainId>:<agentAddress>` whose address matches `agentAddress` (checksummed) — anything else returns `VALIDATION_ERROR`.

### `POST /machines/:machineId/agent-pairings`

Creates a pairing. Requires `agentProof` when pairing verification is enabled (production default). Returns the pairing with a signed session JWT in `pairingToken`. The token is returned once at create; rotate via the sessions endpoint.

```ts theme={"theme":{"light":"github-light-default","dark":"github-dark"}}
type CreateAgentPairingRequest = {
  agentAddress: string;
  agentDid?: string;
  agentProvider: string;
  agentRole: string;
  description?: string;
  agentProof: AgentPairingProof;
  delegationPolicy?: Partial<AgentPairing["delegationPolicy"]>;
};

type CreateAgentPairingResponse = ItemResponse<AgentPairing>;
```

Errors: `AGENT_PAIRING_PROOF_REQUIRED`, `AGENT_PAIRING_PROOF_INVALID`, `AGENT_PAIRING_PROOF_EXPIRED`, `AGENT_PAIRING_UNAVAILABLE`, `MACHINE_NOT_ACTIVE`, `MACHINE_IDENTITY_PROOF_REQUIRED`, `VALIDATION_ERROR`.

### `POST /machines/:machineId/agent-pairings/:pairingId/sessions`

Rotates the session JWT for an active pairing. The agent gets a fresh challenge, signs it, and submits the new proof here. Returns the pairing with a new `pairingToken`.

```ts theme={"theme":{"light":"github-light-default","dark":"github-dark"}}
type CreateAgentPairingSessionRequest = {
  agentProof: AgentPairingProof;
};

type CreateAgentPairingSessionResponse = ItemResponse<AgentPairing>;
```

Default session TTL is 1 hour (`PEAQOS_AGENT_PAIRING_SESSION_TTL_MS`). Market writes reject expired tokens with `AGENT_AUTH_EXPIRED` and reject tokens whose `delegationPolicyHash` no longer matches the persisted policy.

### `PATCH /machines/:machineId/agent-pairings/:pairingId`

Repolicy or pause/resume. Policy changes invalidate the current session token's `delegationPolicyHash`. Rotate the session via `POST .../sessions` after a policy change.

```ts theme={"theme":{"light":"github-light-default","dark":"github-dark"}}
type UpdateAgentPairingRequest = {
  status?: "active" | "paused" | "revoked";
  description?: string | null;
  delegationPolicy?: Partial<AgentPairing["delegationPolicy"]>;
};

type UpdateAgentPairingResponse = ItemResponse<AgentPairing>;
```

### `DELETE /machines/:machineId/agent-pairings/:pairingId`

Revokes the pairing. The current session token is invalidated. Subsequent market-search calls with that token return `AGENT_AUTH_INVALID` or `AGENT_PAIRING_INACTIVE`. If no active pairing exists for a machine, market writes return `AGENT_PAIRING_REQUIRED` (409). If the pairing has open market orders (status not in `confirmed`, `cancelled`, or `failed`), the call returns `OPEN_MARKET_ORDERS` (409) with `orderIds` — cancel or settle those orders first, then retry.

```ts theme={"theme":{"light":"github-light-default","dark":"github-dark"}}
type DeleteAgentPairingResponse = void;
```

## Session JWT claims

The `pairingToken` is an HS256 JWT signed by the orchestrator. Claims:

```ts theme={"theme":{"light":"github-light-default","dark":"github-dark"}}
type AgentPairingSessionClaims = {
  iss: "peaqos-machine-market";
  aud: "machine-agent";
  typ: "agent-pairing-session";
  ver: 1;
  jti: string;
  sid: string;                   // sessionId
  pairingId: string;
  machineId: string;
  machineIdentityRef?: string | null;
  agentAddress: string;
  agentDid: string;
  agentProvider: string;
  agentRole: string;
  delegationPolicyHash: string;
  delegationPolicy: { /* snapshot of policy at issuance */ };
  iat: number;
  nbf: number;
  exp: number;
};
```

The orchestrator re-verifies the signature, expiry, pairing status, and `delegationPolicyHash` against the persisted policy on every market write.

## Delegation policy

```ts theme={"theme":{"light":"github-light-default","dark":"github-dark"}}
type DelegationPolicy = {
  allowedSkillKeys: string[];   // empty = allow all skills
  deniedSkillKeys: string[];    // wins over allowed
  allowedServiceIds: string[];  // empty = allow all listed services
  deniedServiceIds: string[];   // wins over allowed
  perTransactionLimit?: number | null;
  dailySpendLimit?: number | null;
  currency?: string | null;     // ISO 4217 or token symbol
};
```

Enforcement is server-side. Spend-limit violations return `AGENT_SPEND_LIMIT_EXCEEDED` or `AGENT_DAILY_LIMIT_EXCEEDED`. Allow/denylist violations return `AGENT_POLICY_DENIED`.

## Related

* [Machine Markets overview](/peaqos/api-reference/machine-markets-overview)
* [Machine Markets: Machines & identity](/peaqos/api-reference/machine-markets-machines)
* [Machine Markets: Skills, services, search](/peaqos/api-reference/machine-markets-discovery)
* [Scale function](/peaqos/functions/scale)
