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

# SDK: JavaScript

> TypeScript client for peaqOS. Class, static factories, methods, types, constants.

`@peaqos/peaq-os-sdk` is the opinionated TypeScript entry point for the Machine Financial Passport flow. Each capability is exposed both as a `PeaqosClient` instance method and as a standalone function.

## Install

```bash theme={"theme":{"light":"github-light-default","dark":"github-dark"}}
npm install @peaqos/peaq-os-sdk viem dotenv
```

* **Node.js:** ≥ 22
* **TypeScript:** ≥ 5
* **Peer dependency:** `viem` (>= 2.47.10)
* **Exports:** ESM and CJS. No bundler workarounds.

`dotenv` is optional but recommended: `PeaqosClient.fromEnv()` reads from `process.env`, so `import "dotenv/config"` at the top of your entry file is the simplest way to load `.env`.

## Environment variables

| Variable                          | Required   | Default                 | Purpose                                                                                               |
| :-------------------------------- | :--------- | :---------------------- | :---------------------------------------------------------------------------------------------------- |
| `PEAQOS_RPC_URL`                  | Yes        | n/a                     | peaq chain RPC endpoint                                                                               |
| `PEAQOS_PRIVATE_KEY`              | Yes        | n/a                     | Owner private key (`0x` + 64 hex)                                                                     |
| `IDENTITY_REGISTRY_ADDRESS`       | Yes        | n/a                     | Identity Registry contract                                                                            |
| `IDENTITY_STAKING_ADDRESS`        | Yes        | n/a                     | Identity Staking contract                                                                             |
| `EVENT_REGISTRY_ADDRESS`          | Yes        | n/a                     | Event Registry contract                                                                               |
| `MACHINE_NFT_ADDRESS`             | Yes        | n/a                     | Machine NFT contract (LayerZero ONFT)                                                                 |
| `DID_REGISTRY_ADDRESS`            | Yes        | n/a                     | DID Registry precompile                                                                               |
| `BATCH_PRECOMPILE_ADDRESS`        | Yes        | n/a                     | Batch precompile for multi-call bonding                                                               |
| `MACHINE_ACCOUNT_FACTORY_ADDRESS` | No         | n/a                     | `MachineAccountFactory`. Required only for `deploySmartAccount` / `getSmartAccountAddress`.           |
| `MACHINE_NFT_ADAPTER_ADDRESS`     | No         | n/a                     | `MachineNFTAdapter` (LayerZero ONFT adapter). Required only for `bridgeNft` when `source === "peaq"`. |
| `PEAQOS_MCR_API_URL`              | No         | `http://127.0.0.1:8000` | MCR API base URL                                                                                      |
| `OWS_PASSPHRASE`                  | Wallet ops | —                       | OWS vault passphrase for create/import/export wallet helpers when no explicit passphrase is passed    |

### peaq mainnet contracts

Use these addresses for the `PEAQOS_*_ADDRESS` variables when pointing at peaq mainnet. All contracts are UUPS upgradeable proxies; treat the addresses as the current proxy pointers.

| Variable                          | Address                                                              |
| :-------------------------------- | :------------------------------------------------------------------- |
| `IDENTITY_REGISTRY_ADDRESS`       | `0xb53Af985765031936311273599389b5B68aC9956`                         |
| `IDENTITY_STAKING_ADDRESS`        | `0x11c05A650704136786253e8685f56879A202b1C7`                         |
| `EVENT_REGISTRY_ADDRESS`          | `0x43c6AF2E14dc1327dc3cc6c7117D1CD72fffEcbA`                         |
| `MACHINE_NFT_ADDRESS`             | `0x2943F80e9DdB11B9Dd275499C661Df78F5F691F9`                         |
| `DID_REGISTRY_ADDRESS`            | `0x0000000000000000000000000000000000000800` (peaq DID precompile)   |
| `BATCH_PRECOMPILE_ADDRESS`        | `0x0000000000000000000000000000000000000805` (peaq batch precompile) |
| `MACHINE_ACCOUNT_FACTORY_ADDRESS` | `0x4A808d5A90A2c91739E92C70aF19924e0B3D527f`                         |
| `MACHINE_NFT_ADAPTER_ADDRESS`     | `0x9AD5408702EC204441A88589B99ADfC2514AFAE6`                         |

For agung testnet addresses see [Install → Agung testnet contracts](/peaqos/install#agung-testnet-contracts). Bridging is mainnet-only: LayerZero has no DVN routes to agung.

## Client

### `PeaqosClient`

```typescript theme={"theme":{"light":"github-light-default","dark":"github-dark"}}
class PeaqosClient {
  constructor(config: Readonly<PeaqosClientConfig>);

  readonly rpcUrl: string;
  readonly contracts: Readonly<ContractAddresses>;
  readonly apiUrl: string;
  readonly operationalLimits: Readonly<OperationalLimits>;
  readonly publicClient: PublicClient;
  readonly walletClient: WalletClient;

  get address(): Address;

  static fromEnv(): PeaqosClient;
  static fromWallet(
    nameOrId: string,
    passphrase: string | undefined,
    owsSigning: boolean | undefined,
    config: Readonly<Omit<PeaqosClientConfig, "privateKey">>,
    options?: WalletOptions,
  ): Promise<PeaqosClient>;
  static generateKeypair(): Readonly<{ address: Address; privateKey: `0x${string}` }>;

  // Static wallet wrappers (thin pass-throughs to the module-level helpers)
  static createWallet(name: string, passphrase?: string, words?: 12 | 24, options?: WalletOptions): Promise<WalletInfo>;
  static importWallet(name: string, privateKey: string, passphrase?: string, chain?: ImportChain, options?: WalletOptions): Promise<WalletInfo>;
  static importWalletMnemonic(name: string, mnemonic: string, passphrase?: string, index?: number, options?: WalletOptions): Promise<WalletInfo>;
  static listWallets(options?: WalletOptions): Promise<WalletInfo[]>;
  static getWallet(nameOrId: string, options?: WalletOptions): Promise<WalletInfo>;
  static exportWallet(nameOrId: string, passphrase?: string, options?: WalletOptions): Promise<string>;
  static deleteWallet(nameOrId: string, options?: WalletOptions): Promise<void>;

  toJSON(): Record<string, unknown>;
}
```

<ParamField body="config.rpcUrl" type="string" required>
  RPC endpoint (non-empty).
</ParamField>

<ParamField body="config.privateKey" type="string" required>
  `0x` + 64 hex characters.
</ParamField>

<ParamField body="config.contracts" type="ContractAddresses" required>
  All six contract addresses.
</ParamField>

<ParamField body="config.apiUrl" type="string">
  MCR API URL. Defaults to `DEFAULT_API_URL`.
</ParamField>

<ParamField body="config.operationalLimits" type="OperationalLimits">
  Per-tx and rate-limit caps. All-zero disables limits.
</ParamField>

Returns a `PeaqosClient` instance. `toJSON()` and `util.inspect` output redact the private key (`"[REDACTED]"`).

Other RPC endpoints are available. See [Public RPC endpoints](/peaqos/install#public-rpc-endpoints).

<Expandable title="Example">
  ```typescript theme={"theme":{"light":"github-light-default","dark":"github-dark"}}
  import { PeaqosClient } from "@peaqos/peaq-os-sdk";

  const client = new PeaqosClient({
    rpcUrl: "https://peaq.api.onfinality.io/public",
    privateKey: "0xabc...def",
    contracts: {
      identityRegistry: "0x...",
      identityStaking: "0x...",
      eventRegistry: "0x...",
      machineNft: "0x...",
      didRegistry: "0x...",
      batchPrecompile: "0x...",
    },
  });

  console.log(client.address); // checksummed 0x address
  ```
</Expandable>

**Errors:** `ValidationError`: missing/invalid `rpcUrl`, `privateKey`, or any contract address.

### `fromEnv`

```typescript theme={"theme":{"light":"github-light-default","dark":"github-dark"}}
static fromEnv(): PeaqosClient;
```

Returns a fully configured `PeaqosClient`. All required env vars must be set (see [Environment variables](#environment-variables)).

<Expandable title="Example">
  ```typescript theme={"theme":{"light":"github-light-default","dark":"github-dark"}}
  import "dotenv/config";
  import { PeaqosClient } from "@peaqos/peaq-os-sdk";

  const client = PeaqosClient.fromEnv();
  ```
</Expandable>

**Errors:** `ValidationError`: any required env var missing or empty.

### `fromWallet`

```typescript theme={"theme":{"light":"github-light-default","dark":"github-dark"}}
static fromWallet(
  nameOrId: string,
  passphrase: string | undefined,
  owsSigning: boolean | undefined,
  config: Readonly<Omit<PeaqosClientConfig, "privateKey">>,
  options?: WalletOptions,
): Promise<PeaqosClient>;
```

Builds a `PeaqosClient` from an OWS vault wallet. When `owsSigning` is `true` (default), signing routes through OWS: the key is decrypted only per-sign and wiped immediately after. When `false`, the key is decrypted at construction and signing uses viem directly.

<ParamField body="nameOrId" type="string" required>
  Wallet name or UUID in the OWS vault.
</ParamField>

<ParamField body="passphrase" type="string | undefined" required>
  Vault passphrase. Pass `undefined` to fall back to the `OWS_PASSPHRASE` env var.
</ParamField>

<ParamField body="owsSigning" type="boolean | undefined" required>
  Route signing through OWS. Defaults to `true` when `undefined`.
</ParamField>

<ParamField body="config" type="Omit<PeaqosClientConfig, 'privateKey'>" required>
  Client config without `privateKey` (the wallet provides the signer).
</ParamField>

<ParamField body="options" type="WalletOptions">
  Optional vault configuration (e.g. custom `vaultPath`).
</ParamField>

<Expandable title="Example">
  ```typescript theme={"theme":{"light":"github-light-default","dark":"github-dark"}}
  import { PeaqosClient } from "@peaqos/peaq-os-sdk";

  const client = await PeaqosClient.fromWallet("my-machine", "s3cret", true, {
    rpcUrl: "https://peaq.api.onfinality.io/public",
    contracts: {
      identityRegistry: "0x...",
      identityStaking: "0x...",
      eventRegistry: "0x...",
      machineNft: "0x...",
      didRegistry: "0x...",
      batchPrecompile: "0x...",
    },
  });
  ```
</Expandable>

**Errors:** `PeaqosError`: wallet not found, passphrase missing, or OWS signing failure. With `owsSigning: false` a wrong passphrase throws at construction (eager decrypt). With `owsSigning: true` a wrong passphrase surfaces on the first sign call. Key material is never decrypted at construction.

### Wallets (OWS)

Wallet lifecycle helpers (`createWallet`, `importWallet`, `importWalletMnemonic`, `listWallets`, `getWallet`, `exportWallet`, `deleteWallet`, plus the `extractPeaqAddress` utility) back the [Open Wallet Standard](/peaqos/wallets) integration: mnemonic-backed encrypted vault, multi-chain accounts (peaq, Base, Ethereum, Solana, Bitcoin, etc.). Lifecycle helpers are available as module-level imports and as static methods on `PeaqosClient`; the `PeaqosClient.fromWallet` factory wires a vault wallet directly into a client (OWS-native signing by default). The JS package bundles `@open-wallet-standard/core` as a regular dependency; no separate peer install is required. The raw-key constructor and `fromEnv` flow keep working unchanged. Full reference on the [Wallets page](/peaqos/wallets#sdk-methods).

### `generateKeypair`

```typescript theme={"theme":{"light":"github-light-default","dark":"github-dark"}}
static generateKeypair(): Readonly<{
  address: Address;
  privateKey: `0x${string}`;
}>;
```

Returns a frozen object with a fresh `secp256k1` `privateKey` and its derived `address`. No chain interaction. The private key never touches disk.

<Expandable title="Example">
  ```typescript theme={"theme":{"light":"github-light-default","dark":"github-dark"}}
  const kp = PeaqosClient.generateKeypair();
  console.log(kp.address);    // 0x...
  console.log(kp.privateKey); // 0x...
  ```
</Expandable>

### OWS wallet lifecycle

OWS wallet helpers are available as static `PeaqosClient` methods and standalone functions. They derive multi-chain accounts, keep wallet material in an encrypted OWS vault, and return public `WalletInfo` metadata.

```typescript theme={"theme":{"light":"github-light-default","dark":"github-dark"}}
static createWallet(name: string, passphrase?: string, words?: 12 | 24, options?: WalletOptions): Promise<WalletInfo>;
static importWallet(name: string, privateKey: string, passphrase?: string, chain?: ImportChain, options?: WalletOptions): Promise<WalletInfo>;
static importWalletMnemonic(name: string, mnemonic: string, passphrase?: string, index?: number, options?: WalletOptions): Promise<WalletInfo>;
static listWallets(options?: WalletOptions): Promise<WalletInfo[]>;
static getWallet(nameOrId: string, options?: WalletOptions): Promise<WalletInfo>;
static exportWallet(nameOrId: string, passphrase?: string, options?: WalletOptions): Promise<string>;
static deleteWallet(nameOrId: string, options?: WalletOptions): Promise<void>;
static fromWallet(nameOrId: string, passphrase: string | undefined, owsSigning: boolean | undefined, config: Omit<PeaqosClientConfig, "privateKey">, options?: WalletOptions): Promise<PeaqosClient>;
```

OWS wallet helpers are bundled with `@peaqos/peaq-os-sdk`. `createWallet`, `importWallet`, `importWalletMnemonic`, `exportWallet`, and `fromWallet` require a passphrase argument or `OWS_PASSPHRASE`. `options.vaultPath` can point at a custom vault directory. `fromWallet` can sign through OWS (`owsSigning=true`, default) so key material is decrypted only for the signing operation.

<ResponseField name="WalletInfo" type="object">
  Frozen object with `id`, `name`, `createdAt`, `keyType`, `peaqAddress`, and `accounts`. Each account has `accountId`, `address`, `chainId`, `network`, and `derivationPath`.
</ResponseField>

<Expandable title="Example">
  ```typescript theme={"theme":{"light":"github-light-default","dark":"github-dark"}}
  import {
    PeaqosClient,
    IMPORT_CHAIN_EVM,
    extractPeaqAddress,
  } from "@peaqos/peaq-os-sdk";

  const wallet = await PeaqosClient.createWallet("robot-001");
  console.log(wallet.peaqAddress);

  const imported = await PeaqosClient.importWallet(
    "legacy-machine",
    "0xabc...def",
    undefined,
    IMPORT_CHAIN_EVM,
  );

  const all = await PeaqosClient.listWallets();
  const same = await PeaqosClient.getWallet(imported.id);
  console.log(all.length, extractPeaqAddress(same.accounts));

  const client = await PeaqosClient.fromWallet(imported.id, undefined, true, {
    rpcUrl: "https://quicknode1.peaq.xyz",
    contracts: {
      identityRegistry: "0x...",
      identityStaking: "0x...",
      eventRegistry: "0x...",
      machineNft: "0x...",
      didRegistry: "0x0000000000000000000000000000000000000800",
      batchPrecompile: "0x0000000000000000000000000000000000000805",
    },
  });
  console.log(client.address);
  ```
</Expandable>

<Warning>
  `exportWallet` returns mnemonic or private-key material, and `fromWallet` consumes a vault passphrase. Keep these in local administrative tooling; do not expose them through robot control channels.
</Warning>

### Accessors

All accessors are read-only. The private key is held in an ECMAScript `#private` field: never exposed through the public surface and redacted in `JSON.stringify` and `util.inspect`.

| Accessor            | Type                          | Description                                         |
| :------------------ | :---------------------------- | :-------------------------------------------------- |
| `address`           | `Address`                     | Checksummed owner address derived from `privateKey` |
| `rpcUrl`            | `string`                      | Configured RPC endpoint                             |
| `contracts`         | `Readonly<ContractAddresses>` | Frozen contract address map                         |
| `apiUrl`            | `string`                      | MCR API base URL                                    |
| `operationalLimits` | `Readonly<OperationalLimits>` | Per-tx + rate-limit caps                            |
| `publicClient`      | `PublicClient` (viem)         | Read-only chain client                              |
| `walletClient`      | `WalletClient` (viem)         | Signer-bound chain client                           |

***

## Registration

### `registerMachine`

Registers the caller's own address as a machine. Reads `minBond` from the `IdentityRegistry` contract (currently `1 PEAQ`) and sends that value with the transaction.

```typescript theme={"theme":{"light":"github-light-default","dark":"github-dark"}}
async registerMachine(): Promise<number>;
```

<ResponseField name="machineId" type="number">
  The newly allocated machine ID, decoded from the `Registered` event in the transaction receipt.
</ResponseField>

<Expandable title="Example">
  ```typescript theme={"theme":{"light":"github-light-default","dark":"github-dark"}}
  const client = PeaqosClient.fromEnv();
  const machineId = await client.registerMachine();
  console.log(machineId);
  ```
</Expandable>

<Expandable title="Errors">
  * `RuntimeError`: chain revert (`AlreadyRegistered`, `IncorrectBondAmount`, or any other custom error), receipt missing the `Registered` log, or decoded `machineId` out of safe-integer range.

  See [errors](/peaqos/sdk-reference/errors).
</Expandable>

### `registerFor`

Registers a machine on behalf of another address. The caller becomes the proxy operator and supplies the current `minBond` (read from the IdentityRegistry contract) as `msg.value`.

```typescript theme={"theme":{"light":"github-light-default","dark":"github-dark"}}
async registerFor(machineAddress: `0x${string}`): Promise<number>;
```

<ParamField body="machineAddress" type="0x${string}" required>
  Machine EOA. The client's signing address becomes the operator and pays the bond.
</ParamField>

<ResponseField name="machineId" type="number">
  The newly allocated machine ID for the proxied machine.
</ResponseField>

<Expandable title="Example">
  ```typescript theme={"theme":{"light":"github-light-default","dark":"github-dark"}}
  const client = PeaqosClient.fromEnv();
  const { address: machineAddress } = PeaqosClient.generateKeypair();
  const machineId = await client.registerFor(machineAddress);
  ```
</Expandable>

<Expandable title="Errors">
  * `ValidationError`: `machineAddress` is not a valid `0x` address.
  * `RuntimeError`: chain revert (`AlreadyRegistered`, `InvalidMachineAddress` for zero address, `IncorrectBondAmount`, or any other custom error), receipt missing the `Registered` log, or decoded `machineId` out of safe-integer range.

  The zero address is allowed through to the chain so the user-facing message comes from the `InvalidMachineAddress` revert mapping (contains `"zero address"`).
</Expandable>

***

## Gas Station

<Steps>
  <Step title="Setup 2FA">
    Call [`setupFaucet2FA`](#setupfaucet2fa) to enroll the owner.
  </Step>

  <Step title="Confirm 2FA">
    Call [`confirmFaucet2FA`](#confirmfaucet2fa) with a TOTP from the authenticator.
  </Step>

  <Step title="Fund">
    Call [`fundFromGasStation`](#fundfromgasstation) to send gas to a machine wallet.
  </Step>
</Steps>

### `setupFaucet2FA`

Enrolls an owner address for 2FA with the Gas Station. Returns a QR code URL (expires after \~2 minutes).

```typescript theme={"theme":{"light":"github-light-default","dark":"github-dark"}}
async setupFaucet2FA(
  ownerAddress: string,
  faucetBaseUrl: string,
  format?: FaucetQrFormat,
): Promise<FaucetSetupResponse>;
```

<ParamField body="ownerAddress" type="string" required>
  Owner to enroll (SS58 or hex).
</ParamField>

<ParamField body="faucetBaseUrl" type="string" required>
  Gas Station base URL.
</ParamField>

<ParamField body="format" type="'svg' | 'png'">
  QR format. Defaults to `"svg"`.
</ParamField>

<ResponseField name="FaucetSetupResponse" type="object">
  <Expandable title="properties">
    <ResponseField name="ownerAddress" type="string" required>
      The enrolled owner address.
    </ResponseField>

    <ResponseField name="otpauthUri" type="string" required>
      OTP auth URI for authenticator apps.
    </ResponseField>

    <ResponseField name="qrImageUrl" type="string" required>
      QR code image URL. Expires after \~2 minutes. Render immediately, never persist.
    </ResponseField>
  </Expandable>
</ResponseField>

<Expandable title="Example">
  ```typescript theme={"theme":{"light":"github-light-default","dark":"github-dark"}}
  const setup = await client.setupFaucet2FA(
    client.address,
    "https://depinstation.peaq.network",
  );
  console.log(setup.otpauthUri);
  console.log(setup.qrImageUrl);
  ```
</Expandable>

**Errors:** `ValidationError` on empty args. `RuntimeError` for `INVALID_OWNER_ADDRESS`, `INVALID_PAYLOAD`, `QR_GENERATION_FAILED`, unexpected envelope, or HTTP failure. See [errors](/peaqos/sdk-reference/errors).

### `confirmFaucet2FA`

Confirms 2FA enrollment with a fresh TOTP code.

```typescript theme={"theme":{"light":"github-light-default","dark":"github-dark"}}
async confirmFaucet2FA(
  ownerAddress: string,
  faucetBaseUrl: string,
  twoFactorCode: string,
): Promise<void>;
```

<ParamField body="ownerAddress" type="string" required>
  Owner address being confirmed.
</ParamField>

<ParamField body="faucetBaseUrl" type="string" required>
  Gas Station base URL.
</ParamField>

<ParamField body="twoFactorCode" type="string" required>
  Fresh 6-digit TOTP.
</ParamField>

Returns `void` on successful activation.

<Expandable title="Example">
  ```typescript theme={"theme":{"light":"github-light-default","dark":"github-dark"}}
  await client.confirmFaucet2FA(
    client.address,
    "https://depinstation.peaq.network",
    "123456",
  );
  ```
</Expandable>

**Errors:** `ValidationError` on any empty argument. `RuntimeError` for `INVALID_2FA`, `2FA_NOT_CONFIGURED`, `2FA_LOCKED`, unexpected envelope, or transport failure.

### `fundFromGasStation`

Sends gas tokens to a machine wallet. Returns a discriminated union on `status`.

```typescript theme={"theme":{"light":"github-light-default","dark":"github-dark"}}
async fundFromGasStation(
  params: FundFromGasStationParams,
  faucetBaseUrl: string,
): Promise<FaucetFundResponse>;
```

<ParamField body="params.ownerAddress" type="string" required>
  2FA-enrolled owner (SS58 or hex).
</ParamField>

<ParamField body="params.targetWalletAddress" type="string" required>
  Machine EOA to fund.
</ParamField>

<ParamField body="params.chainId" type="string" required>
  Faucet-configured chain identifier (e.g., `"peaq"`).
</ParamField>

<ParamField body="params.twoFactorCode" type="string" required>
  Current TOTP.
</ParamField>

<ParamField body="params.requestId" type="string">
  UUID idempotency key. Auto-generated if omitted.
</ParamField>

<ResponseField name="FaucetFundResponse" type="FaucetFundSuccessResponse | FaucetFundSkippedResponse">
  <Expandable title="Success response">
    <ResponseField name="status" type="'success'" required />

    <ResponseField name="txHash" type="string" required>
      Transaction hash.
    </ResponseField>

    <ResponseField name="fundedAmount" type="string" required>
      Decimal wei. Never a JS number.
    </ResponseField>

    <ResponseField name="requestId" type="string" required />
  </Expandable>

  <Expandable title="Skipped response (sufficient balance)">
    <ResponseField name="status" type="'skipped'" required />

    <ResponseField name="currentBalance" type="string" required />

    <ResponseField name="minGasBalance" type="string" required />

    <ResponseField name="requestId" type="string" required />
  </Expandable>
</ResponseField>

<Note>
  **Cross-SDK behavior:** The JS SDK echoes `requestId` back in both success and skipped responses. The Python SDK does not include `request_id` in the response at all — it is a request-side idempotency key only. Do not write code that reads `requestId` from the response and expects it to work in both SDKs.
</Note>

<Expandable title="Example">
  ```typescript theme={"theme":{"light":"github-light-default","dark":"github-dark"}}
  const result = await client.fundFromGasStation(
    {
      ownerAddress: client.address,
      targetWalletAddress: machineAddress,
      chainId: "peaq",
      twoFactorCode: "123456",
    },
    "https://depinstation.peaq.network",
  );

  if (result.status === "success") {
    console.log("tx:", result.txHash, "amount:", result.fundedAmount);
  } else {
    console.log("already funded; balance:", result.currentBalance);
  }
  ```
</Expandable>

<Expandable title="Errors">
  * `ValidationError`: missing/empty required fields, `requestId` not a UUID, or `faucetBaseUrl` empty.
  * `RuntimeError`: any documented faucet code: `INVALID_2FA`, `2FA_NOT_CONFIGURED`, `2FA_NOT_ACTIVE`, `2FA_LOCKED`, `DUPLICATE_REQUEST`, `REQUEST_ALREADY_PROCESSED`, `RATE_LIMITED`, `CAP_EXCEEDED_OWNER`, `CAP_EXCEEDED_WALLET`, `TRANSFER_FAILED`.

  See [errors](/peaqos/sdk-reference/errors) for the full code → cause → retry table.
</Expandable>

***

## NFT & DID

Machine NFT minting, token-ID lookup, and the two canonical DID attribute writers. The DID writes batch six (machine) or two (proxy) attributes into a single atomic `batchAll` transaction via the peaq Batch precompile.

### `mintNft`

Mints a Machine NFT on the MachineNFT contract for a registered, bonded machine. Returns the transaction hash.

```typescript theme={"theme":{"light":"github-light-default","dark":"github-dark"}}
async mintNft(
  machineId: number,
  recipient: `0x${string}`,
): Promise<Hex>;
```

<ParamField body="machineId" type="number" required>
  Registered machine ID. Must be a positive integer.
</ParamField>

<ParamField body="recipient" type="0x${string}" required>
  Address that will own the minted NFT.
</ParamField>

<Expandable title="Example">
  ```typescript theme={"theme":{"light":"github-light-default","dark":"github-dark"}}
  const txHash = await client.mintNft(machineId, client.address);
  ```
</Expandable>

<Expandable title="Errors">
  * `ValidationError`: `machineId` not positive, `recipient` not a valid `0x` address, or `client.contracts.machineNft` not a valid address.
  * `RuntimeError`: chain revert (`MachineNotBonded`, `AlreadyMinted`, `NotMachineOwner`, `MachineNotFound`, `InvalidAddress`) or transaction failure.

  See [errors](/peaqos/sdk-reference/errors).
</Expandable>

### `tokenIdOf`

Reads the NFT token ID assigned to a registered machine via a view call.

```typescript theme={"theme":{"light":"github-light-default","dark":"github-dark"}}
async tokenIdOf(machineId: number): Promise<number>;
```

<ParamField body="machineId" type="number" required>
  Registered machine ID. Must be a positive integer.
</ParamField>

<ResponseField name="tokenId" type="number">
  The NFT token ID, or `0` if no NFT has been minted for this machine.
</ResponseField>

<Expandable title="Example">
  ```typescript theme={"theme":{"light":"github-light-default","dark":"github-dark"}}
  const tokenId = await client.tokenIdOf(machineId);
  if (tokenId === 0) {
    console.log("No NFT minted yet");
  }
  ```
</Expandable>

<Expandable title="Errors">
  * `ValidationError`: `machineId` not a positive integer, or `client.contracts.machineNft` not a valid address.
  * `RuntimeError`: contract returns a token ID beyond `Number.MAX_SAFE_INTEGER`.
</Expandable>

<Note>
  **Cross-SDK behavior:** The JS SDK returns `0` when no NFT has been minted for the machine. The Python SDK raises `RpcError` instead (the contract reverts). In polyglot codebases, check for `0` in JS and catch `RpcError` in Python — do not assume the same pattern works in both.
</Note>

### `writeMachineDIDAttributes`

Atomically writes the six canonical Machine DID attributes (`machineId`, `nftTokenId`, `operator`, `documentation_url`, `data_api`, `data_visibility`) to the caller's DID via a single batched transaction.

```typescript theme={"theme":{"light":"github-light-default","dark":"github-dark"}}
async writeMachineDIDAttributes(
  params: WriteMachineDIDParams,
): Promise<Hex>;
```

<ParamField body="params.machineId" type="number" required>
  Registered machine ID.
</ParamField>

<ParamField body="params.nftTokenId" type="number" required>
  NFT token ID assigned to the machine.
</ParamField>

<ParamField body="params.operatorDid" type="string" required>
  Operator DID reference. May be an empty string. ASCII, ≤ 2560 bytes.
</ParamField>

<ParamField body="params.documentationUrl" type="string" required>
  Non-empty ASCII URL, ≤ 2560 bytes.
</ParamField>

<ParamField body="params.dataApi" type="string" required>
  Non-empty ASCII URL for the machine's data API, ≤ 2560 bytes.
</ParamField>

<ParamField body="params.dataVisibility" type="'public' | 'private' | 'onchain'" required>
  Visibility setting.
</ParamField>

<Expandable title="Example">
  ```typescript theme={"theme":{"light":"github-light-default","dark":"github-dark"}}
  const txHash = await client.writeMachineDIDAttributes({
    machineId: 42,
    nftTokenId: 1,
    operatorDid: "",
    documentationUrl: "https://docs.example.com",
    dataApi: "https://api.example.com",
    dataVisibility: "public",
  });
  ```
</Expandable>

### `writeProxyDIDAttributes`

Atomically writes the two canonical Proxy DID attributes (`machineId`, `machines`) to the caller's DID.

```typescript theme={"theme":{"light":"github-light-default","dark":"github-dark"}}
async writeProxyDIDAttributes(
  params: WriteProxyDIDParams,
): Promise<Hex>;
```

<ParamField body="params.proxyMachineId" type="number" required>
  The proxy operator's registered machine ID.
</ParamField>

<ParamField body="params.machineIds" type="readonly number[]" required>
  Non-empty list of positive machine IDs managed by this proxy. The JSON-encoded array must be ≤ 2560 bytes.
</ParamField>

<Expandable title="Example">
  ```typescript theme={"theme":{"light":"github-light-default","dark":"github-dark"}}
  const txHash = await client.writeProxyDIDAttributes({
    proxyMachineId: 10,
    machineIds: [42, 43, 44],
  });
  ```
</Expandable>

### `readAttribute`

Reads a single DID attribute directly from the peaq DID precompile. Most consumers should prefer the [`/machine/{did}` API](/peaqos/api-reference/get-machine), which composes the full attribute set; this helper is the on-chain escape hatch.

```typescript theme={"theme":{"light":"github-light-default","dark":"github-dark"}}
async function readAttribute(
  client: PeaqosClient,
  did: Address,
  name: string,
): Promise<DIDAttributeResult>;
```

<ParamField body="did" type="Address" required>
  The DID account address whose attribute is being read (typically a machine address, but any DID-bearing EOA works).
</ParamField>

<ParamField body="name" type="string" required>
  Attribute key, e.g. `"machineId"`, `"data_visibility"`, `"machines"`.
</ParamField>

<ResponseField name="DIDAttributeResult" type="object">
  `{ name: string; value: string; validity: number; created: bigint }`. `validity` is `0` when the attribute has no expiry. Throws `RuntimeError` if the attribute does not exist on the precompile.
</ResponseField>

### `encodeAddAttribute`

Encodes ABI call data for the DID precompile's `addAttribute(didAccount, name, value, validFor)` function. Useful when constructing smart-account `executeBatch` calls that touch the DID precompile alongside other contracts. `writeMachineDIDAttributes` and `writeProxyDIDAttributes` use this helper internally; reach for it directly only when composing custom batch flows.

```typescript theme={"theme":{"light":"github-light-default","dark":"github-dark"}}
function encodeAddAttribute(
  didAccount: Address,
  name: string,
  value: string,
  validFor: number,
): Hex;
```

<ParamField body="didAccount" type="Address" required>
  Address of the DID account being written. On-chain this must equal `msg.sender` of the resulting precompile call.
</ParamField>

<ParamField body="name" type="string" required>
  Attribute name. ASCII only, ≤ 64 bytes.
</ParamField>

<ParamField body="value" type="string" required>
  Attribute value. ASCII only, ≤ 2560 bytes.
</ParamField>

<ParamField body="validFor" type="number" required>
  Validity period in blocks. `0` means no expiry. Must be a non-negative integer in the `uint32` range.
</ParamField>

<ResponseField name="Hex" type="string">
  ABI-encoded call data. Throws `ValidationError` if any constraint is violated.
</ResponseField>

***

## Smart accounts

ERC-4337 smart accounts deployed via the `MachineAccountFactory`. Requires the client to be constructed with a `machineAccountFactory` address (or the `MACHINE_ACCOUNT_FACTORY_ADDRESS` env var via `fromEnv`).

### `deploySmartAccount`

Deploys a smart account via `MachineAccountFactory.createAccount` and returns the deployed address.

```typescript theme={"theme":{"light":"github-light-default","dark":"github-dark"}}
async deploySmartAccount(
  params: DeploySmartAccountParams,
): Promise<Address>;
```

<ParamField body="params.owner" type="Address" required>
  EOA that will own the smart account.
</ParamField>

<ParamField body="params.machine" type="Address" required>
  Machine EOA the account is scoped to.
</ParamField>

<ParamField body="params.salt" type="number" required>
  Non-negative CREATE2 salt.
</ParamField>

<Expandable title="Example">
  ```typescript theme={"theme":{"light":"github-light-default","dark":"github-dark"}}
  const address = await client.deploySmartAccount({
    owner: client.address,
    machine: machineAddress,
    salt: 0,
  });
  ```
</Expandable>

### `getSmartAccountAddress`

Read-only equivalent: computes the CREATE2 address for the given `(owner, machine, salt)` without deploying. Returns the same address `deploySmartAccount` would produce.

```typescript theme={"theme":{"light":"github-light-default","dark":"github-dark"}}
async getSmartAccountAddress(
  params: DeploySmartAccountParams,
): Promise<Address>;
```

Same parameters as `deploySmartAccount`. No transaction, no gas.

<Expandable title="Example">
  ```typescript theme={"theme":{"light":"github-light-default","dark":"github-dark"}}
  const predicted = await client.getSmartAccountAddress({
    owner: client.address,
    machine: machineAddress,
    salt: 0,
  });
  ```
</Expandable>

***

## Bridge

<Note>
  Supported routes: peaq ↔ Base. Additional peaqOS chains are added as peer contracts deploy. See [Machine NFT cross-chain portability](/peaqos/concepts/machine-nft#cross-chain-portability).
</Note>

LayerZero v2 Machine NFT bridging between peaq and Base. Requires the `machineNftAdapter` address (or `MACHINE_NFT_ADAPTER_ADDRESS`) when sending from peaq. The SDK's `source` / `destination` literal union expands as peer contracts deploy on new chains.

### `bridgeNft`

Bridges a Machine NFT from `source` to `destination`. When `source === "base"`, `baseRpcUrl` and `baseNftAddress` are required so the SDK can build a per-call viem client for the Base side.

On the peaq→Base path the SDK runs an ERC-721 approval pre-flight: it checks `MachineNFT.getApproved(tokenId)` and submits a one-shot `approve(adapter, tokenId)` if the token isn't already cleared for the adapter. The Base→peaq path uses burn-and-unlock and needs no approval. Either way, callers don't handle approvals themselves.

```typescript theme={"theme":{"light":"github-light-default","dark":"github-dark"}}
async bridgeNft(params: BridgeNftParams): Promise<Hex>;
```

<ParamField body="params.tokenId" type="number" required>
  Positive NFT id to bridge.
</ParamField>

<ParamField body="params.source" type="'peaq' | 'base'" required>
  Origin chain.
</ParamField>

<ParamField body="params.destination" type="'peaq' | 'base'" required>
  Target chain (must differ from `source`).
</ParamField>

<ParamField body="params.recipient" type="Address" required>
  Destination-chain recipient.
</ParamField>

<ParamField body="params.extraOptions" type="Hex">
  Raw LayerZero v2 `extraOptions` bytes. Defaults to `"0x"` (the contract's enforced options).
</ParamField>

<ParamField body="params.baseRpcUrl" type="string">
  Base RPC URL. Required only when `source === "base"`.
</ParamField>

<ParamField body="params.baseNftAddress" type="Address">
  `MachineNFTBase` address on Base. Required only when `source === "base"`.
</ParamField>

<ResponseField name="txHash" type="Hex">
  The source-chain transaction hash.
</ResponseField>

<Expandable title="Example">
  ```typescript theme={"theme":{"light":"github-light-default","dark":"github-dark"}}
  const txHash = await client.bridgeNft({
    tokenId: 42,
    source: "peaq",
    destination: "base",
    recipient: "0xabc...",
  });
  ```
</Expandable>

### `waitForBridgeArrival`

Static method that polls the destination chain's `MachineNFT.ownerOf(tokenId)` every 10 seconds until a non-zero owner returns or the timeout elapses. No `PeaqosClient` instance required.

```typescript theme={"theme":{"light":"github-light-default","dark":"github-dark"}}
static async waitForBridgeArrival(
  params: WaitForBridgeArrivalParams,
): Promise<boolean>;
```

<ParamField body="params.dstRpcUrl" type="string" required>
  Destination-chain RPC endpoint.
</ParamField>

<ParamField body="params.dstNftAddress" type="Address" required>
  `MachineNFT` contract address on the destination.
</ParamField>

<ParamField body="params.tokenId" type="number" required>
  The NFT id expected to arrive.
</ParamField>

<ParamField body="params.timeout" type="number">
  Wait budget in seconds. Defaults to 300 (5 min).
</ParamField>

<ParamField body="params.signal" type="AbortSignal">
  Optional abort signal. When aborted, the poll stops immediately with a `RuntimeError` code `ABORTED`.
</ParamField>

<Expandable title="Example">
  ```typescript theme={"theme":{"light":"github-light-default","dark":"github-dark"}}
  const arrived = await PeaqosClient.waitForBridgeArrival({
    dstRpcUrl: "https://mainnet.base.org",
    dstNftAddress: "0x...",
    tokenId: 42,
    timeout: 120,
  });
  ```
</Expandable>

***

## Events (Qualify)

### `submitEvent`

Submits a single event to `EventRegistry`. Validates and normalizes the payload, then calls the contract. Returns the transaction hash and the computed `dataHash`.

```typescript theme={"theme":{"light":"github-light-default","dark":"github-dark"}}
async submitEvent(
  params: SubmitEventParams,
): Promise<{ txHash: Hex; dataHash: Hex }>;
```

Param shape matches [`validateSubmitEventParams`](#validatesubmiteventparams) below. `value` is an **ISO 4217 minor-unit integer** (cents for USD/HKD, whole units for JPY/KRW/VND, thousandths for BHD). `currency` is required on revenue events (`^[A-Z0-9]{3,10}$`) and must be `""` on activity events; the SDK applies a smart default (revenue → `"USD"`, activity → `""`) when omitted on `submitEvent`. `batchSubmitEvents` is strict: every event must carry `currency` explicitly.

<Expandable title="Example">
  ```typescript theme={"theme":{"light":"github-light-default","dark":"github-dark"}}
  const { txHash, dataHash } = await client.submitEvent({
    machineId: 1024,
    eventType: EVENT_TYPE_REVENUE,
    value: 12500,                     // $125.00 in cents
    currency: "USD",
    timestamp: Math.floor(Date.now() / 1000),
    rawData: new Uint8Array([1, 2, 3]),
    trustLevel: TRUST_ON_CHAIN_VERIFIABLE,
    sourceChainId: SUPPORTED_CHAIN_IDS.peaq,
    sourceTxHash: null,
    metadata: new Uint8Array([]),
  });
  ```
</Expandable>

<Expandable title="Errors">
  * `ValidationError`: any `params` field fails validation.
  * `ValueCapExceeded` / `RateLimitExceeded`: client-side operational limits hit.
  * `RuntimeError`: chain revert or receipt failure.
</Expandable>

### `batchSubmitEvents`

Submits multiple events atomically through the peaq Batch precompile. All events land in the same transaction: all succeed or all revert.

```typescript theme={"theme":{"light":"github-light-default","dark":"github-dark"}}
async batchSubmitEvents(
  events: ReadonlyArray<SubmitEventParams>,
): Promise<Hex[]>;
```

<ParamField body="events" type="readonly SubmitEventParams[]" required>
  Non-empty list of event payloads. Each element is validated individually before submission.
</ParamField>

<ResponseField name="txHashes" type="Hex[]">
  One transaction hash per input event. All hashes are identical (same batch tx).
</ResponseField>

<Expandable title="Errors">
  * `ValidationError`: list is empty, or any event fails validation.
  * `ValueCapExceeded` / `RateLimitExceeded`: operational limits hit for any event in the batch.
  * `RuntimeError`: batch revert or transport failure.
</Expandable>

### `validateSubmitEventParams`

```typescript theme={"theme":{"light":"github-light-default","dark":"github-dark"}}
function validateSubmitEventParams(params: SubmitEventParams): void;
```

<ParamField body="machineId" type="number" required>
  Machine ID returned by `registerMachine` / `registerFor`.
</ParamField>

<ParamField body="eventType" type="0 | 1" required>
  `0` revenue, `1` activity.
</ParamField>

<ParamField body="value" type="number" required>
  Non-negative ISO 4217 minor-unit integer. Cents for USD/HKD, whole units for JPY/KRW/VND, thousandths for BHD. Activity events: any non-negative integer or `0`.
</ParamField>

<ParamField body="currency" type="string">
  Revenue: 3-10 uppercase alphanumeric (e.g. `"USD"`, `"HKD"`, `"JPY"`). Activity: must be `""`. Omit to apply the SDK smart default (revenue → `"USD"`, activity → `""`); `batchSubmitEvents` requires it explicitly.
</ParamField>

<ParamField body="timestamp" type="number" required>
  Unix seconds.
</ParamField>

<ParamField body="rawData" type="Uint8Array | null" required>
  Off-chain payload hashed into `dataHash`.
</ParamField>

<ParamField body="trustLevel" type="0 | 1 | 2" required>
  `0` self-reported, `1` on-chain verifiable, `2` hardware-signed.
</ParamField>

<ParamField body="sourceChainId" type="number" required>
  Originating chain. Use `SUPPORTED_CHAIN_IDS.peaq` for local.
</ParamField>

<ParamField body="sourceTxHash" type="Hex | null" required>
  Cross-chain tx hash when applicable.
</ParamField>

<ParamField body="metadata" type="Uint8Array" required>
  Arbitrary bytes stored on-chain alongside the event. Use empty bytes when no metadata is needed.
</ParamField>

Returns `void`. Throws `ValidationError` on any invariant violation.

<Expandable title="Example">
  ```typescript theme={"theme":{"light":"github-light-default","dark":"github-dark"}}
  import {
    validateSubmitEventParams,
    EVENT_TYPE_REVENUE,
    TRUST_ON_CHAIN_VERIFIABLE,
    SUPPORTED_CHAIN_IDS,
  } from "@peaqos/peaq-os-sdk";

  validateSubmitEventParams({
    machineId: 1024,
    eventType: EVENT_TYPE_REVENUE,
    value: 1250,                      // $12.50 in cents
    currency: "USD",
    timestamp: Math.floor(Date.now() / 1000),
    rawData: new Uint8Array([1, 2, 3]),
    trustLevel: TRUST_ON_CHAIN_VERIFIABLE,
    sourceChainId: SUPPORTED_CHAIN_IDS.base,
    sourceTxHash: "0xabc...",
    metadata: new Uint8Array([]),
  });
  ```
</Expandable>

### `computeDataHash`

```typescript theme={"theme":{"light":"github-light-default","dark":"github-dark"}}
function computeDataHash(rawData: Uint8Array): Hex;
```

<ParamField body="rawData" type="Uint8Array" required>
  Off-chain payload bytes to hash.
</ParamField>

Returns a `keccak256` hash as `0x` + 64 hex characters.

<Expandable title="Example">
  ```typescript theme={"theme":{"light":"github-light-default","dark":"github-dark"}}
  import { computeDataHash } from "@peaqos/peaq-os-sdk";

  const dataHash = computeDataHash(new TextEncoder().encode("revenue: $12.50"));
  console.log(dataHash);
  ```
</Expandable>

### `checkOperationalLimits`

```typescript theme={"theme":{"light":"github-light-default","dark":"github-dark"}}
function checkOperationalLimits(
  params: { machineId: number; value: number },
  limits: OperationalLimits,
  tracker: EventTracker | null,
): void;
```

<ParamField body="params" type="{ machineId: number; value: number }" required>
  Machine ID and event value.
</ParamField>

<ParamField body="limits" type="OperationalLimits" required>
  Configured `maxValuePerTx`, `rateLimitMaxEvents`, `rateLimitWindowSeconds`.
</ParamField>

<ParamField body="tracker" type="EventTracker | null" required>
  Current rate-tracking state for the machine. Pass `null` if you are not tracking window state. `EventTracker` is `{ machineId: number; count: number; windowStart: number }`.
</ParamField>

Returns `void`. Throws `ValueCapExceeded` or `RateLimitExceeded` on limit violation.

<Expandable title="Example">
  ```typescript theme={"theme":{"light":"github-light-default","dark":"github-dark"}}
  import { checkOperationalLimits } from "@peaqos/peaq-os-sdk";

  checkOperationalLimits(
    { machineId: 1, value: 100 },
    client.operationalLimits,
    { machineId: 1, count: 50, windowStart: Date.now() / 1000 - 1800 },
  );
  ```
</Expandable>

***

## Queries

Read-only helpers backed by the off-chain MCR API server (`client.apiUrl`). Each function validates the DID, issues a single `GET`, and returns a frozen, shape-checked response. All three accept an optional `GetJsonOptions` with `timeoutMs` (default 30 000 ms) and a caller `AbortSignal`.

### `queryMcr`

Fetches the Machine Credit Rating for a machine DID. See [`GET /mcr/{did}`](/peaqos/api-reference/get-mcr).

```typescript theme={"theme":{"light":"github-light-default","dark":"github-dark"}}
async function queryMcr(
  client: PeaqosClient,
  did: string,
  options?: GetJsonOptions,
): Promise<MCRResponse>;
```

<ParamField body="did" type="string" required>
  Machine DID. Must start with `did:peaq:0x`.
</ParamField>

<ParamField body="options.timeoutMs" type="number">
  Request budget in ms. Defaults to 30 000.
</ParamField>

<ParamField body="options.signal" type="AbortSignal">
  Caller abort signal. Either signal aborting wins.
</ParamField>

<ResponseField name="MCRResponse" type="object">
  Frozen object with camelCase fields: `did`, `machineId`, `mcrScore` (number, 0–100), `mcr` (`"AAA" | "AA" | "A" | "BBB" | "BB" | "B" | "NR" | "Provisioned"`), `mcrDegraded` (boolean: `true` when ≥1 scored event used a stale or unavailable FX source), `bondStatus` (`"bonded" | "unbonded"`), `negativeFlag` (boolean: `true` when the machine has been flagged for negative behaviour; consumers should down-rank or alert independently of the numeric score), `eventCount`, `revenueEventCount`, `activityEventCount`, `revenueTrend` (`"up" | "stable" | "down" | "insufficient"`), `totalRevenue` (integer USD cents; divide by 100 for display), `averageRevenuePerEvent` (USD cents as float; divide by 100 for display), `lastUpdated` (unix seconds or `null`).
</ResponseField>

<Note>
  The MCR API returns `mcr_score: null` while a machine is still `Provisioned` or has rating `NR`. The JS SDK coerces this to `0` so `mcrScore` is always a number.
</Note>

<Expandable title="Example">
  ```typescript theme={"theme":{"light":"github-light-default","dark":"github-dark"}}
  import { queryMcr } from "@peaqos/peaq-os-sdk";

  const score = await queryMcr(client, "did:peaq:0xMachineAddress", {
    timeoutMs: 5_000,
  });
  console.log(`${score.mcrScore} → ${score.mcr}`);
  ```
</Expandable>

<Expandable title="Errors">
  * `ValidationError`: `did` does not start with `did:peaq:0x`.
  * `RuntimeError`: HTTP 404 (`NOT_FOUND`), 503 (`SERVICE_UNAVAILABLE`), other 5xx (`SERVER_ERROR`), other non-2xx (`HTTP_ERROR`), timeout (`TIMEOUT`), caller abort (`ABORTED`), transport failure (`NETWORK_ERROR`), or malformed body (`BAD_RESPONSE`).
</Expandable>

### `queryMachine`

Fetches the full machine profile (NFT Metadata JSON v1.0) for a DID. The SDK strictly validates the response against `MachineProfileResponse` and throws `BAD_RESPONSE` for any missing or malformed required field. See [`GET /machine/{did}`](/peaqos/api-reference/get-machine).

```typescript theme={"theme":{"light":"github-light-default","dark":"github-dark"}}
async function queryMachine(
  client: PeaqosClient,
  did: string,
  options?: GetJsonOptions,
): Promise<MachineProfileResponse>;
```

<ParamField body="did" type="string" required>
  Machine DID. Must start with `did:peaq:0x`.
</ParamField>

<ParamField body="options" type="GetJsonOptions">
  Optional `timeoutMs` (default 30 000) and caller `signal`.
</ParamField>

<ResponseField name="MachineProfileResponse" type="object">
  Frozen, strictly-validated machine profile. Top-level fields: `schema_version` (string) and `name` (string). The `peaqos` sub-object always carries `machine_id`, `did`, `operator`, `mcr`, `mcr_score`, `bond_status`, `negative_flag`, `event_count`, `data_visibility`, and `documentation_url`. Visibility-dependent extras: `data_api`, `event_data`, `partner_data`, `partner_data_error`. The SDK throws `BAD_RESPONSE` if the server returns anything that fails the schema guard. See [`GET /machine/{did}`](/peaqos/api-reference/get-machine) for full field semantics.
</ResponseField>

<Expandable title="Example">
  ```typescript theme={"theme":{"light":"github-light-default","dark":"github-dark"}}
  import { queryMachine } from "@peaqos/peaq-os-sdk";

  const profile = await queryMachine(client, "did:peaq:0xMachineAddress");
  console.log(profile.name, profile.peaqos.mcr, profile.peaqos.mcr_score);
  ```
</Expandable>

<Expandable title="Errors">
  * `ValidationError`: `did` does not start with `did:peaq:0x`.
  * `RuntimeError`: same HTTP / transport codes as `queryMcr`; `BAD_RESPONSE` if any required field on `MachineProfileResponse` or its `peaqos` sub-object is missing or wrong-typed.
</Expandable>

### `queryOperatorMachines`

Fetches the fleet of machines managed by a proxy operator. Each machine summary carries its DID, machine ID, score, rating tier, and `negativeFlag`; the response also includes pagination metadata. See [`GET /operator/{did}/machines`](/peaqos/api-reference/get-operator-machines).

```typescript theme={"theme":{"light":"github-light-default","dark":"github-dark"}}
async function queryOperatorMachines(
  client: PeaqosClient,
  did: string,
  options?: GetJsonOptions,
): Promise<OperatorMachinesResponse>;
```

<ParamField body="did" type="string" required>
  Operator DID. Must start with `did:peaq:0x`.
</ParamField>

<ParamField body="options" type="GetJsonOptions">
  Optional `timeoutMs` (default 30 000) and caller `signal`.
</ParamField>

<ResponseField name="OperatorMachinesResponse" type="object">
  Frozen object with `operatorDid`, a frozen `machines` array, and a `pagination` object. Each machine entry exposes `did`, `machineId`, `mcrScore` (number, 0–100; `null` coerced to `0`), `mcr` rating, and `negativeFlag` (boolean: `true` when the machine has been flagged for negative behaviour). `pagination` carries `offset`, `limit`, and `total` (all non-negative integers).
</ResponseField>

<Expandable title="Example">
  ```typescript theme={"theme":{"light":"github-light-default","dark":"github-dark"}}
  import { queryOperatorMachines } from "@peaqos/peaq-os-sdk";

  const fleet = await queryOperatorMachines(client, "did:peaq:0xProxyAddress");
  for (const m of fleet.machines) {
    console.log(`${m.did} → ${m.mcrScore} (${m.mcr}), negative=${m.negativeFlag}`);
  }
  console.log(`showing ${fleet.machines.length} of ${fleet.pagination.total}`);
  ```
</Expandable>

<Expandable title="Errors">
  * `ValidationError`: `did` does not start with `did:peaq:0x`.
  * `RuntimeError`: same HTTP / transport codes as `queryMcr`; `BAD_RESPONSE` if the body or any `machines` entry is malformed.
</Expandable>

***

## Error classes

See [errors](/peaqos/sdk-reference/errors) for the full hierarchy and the 20-code faucet table.

```typescript theme={"theme":{"light":"github-light-default","dark":"github-dark"}}
import {
  PeaqosError,
  RuntimeError,
  ValidationError,
  ValueCapExceeded,
  RateLimitExceeded,
} from "@peaqos/peaq-os-sdk";
```

***

## Type exports

<Accordion title="All exported types">
  ```typescript theme={"theme":{"light":"github-light-default","dark":"github-dark"}}
  import type {
    // Client
    PeaqosClientConfig,
    ContractAddresses,
    OperationalLimits,

    // Events
    SubmitEventParams,
    MachineEvent,
    EventType,
    TrustLevel,
    EventTracker,

    // Identity
    DataVisibility,
    BondStatus,

    // API responses
    MCRResponse,
    MachineProfileResponse,
    OperatorMachinesResponse,
    MCRRating,
    RevenueTrend,

    // DID
    WriteMachineDIDParams,
    WriteProxyDIDParams,
    DIDWriteAttribute,
    DIDAttributeResult,

    // Wallet
    WalletInfo,
    AccountInfo,
    KeyType,
    ImportChain,
    WalletOptions,

    // Query options
    GetJsonOptions,

    // Methods
    DeploySmartAccountParams,
    BridgeNftParams,
    WaitForBridgeArrivalParams,

    // Faucet
    FaucetErrorCode,
    FaucetFundResponse,
    FaucetFundSkippedResponse,
    FaucetFundSuccessResponse,
    FaucetQrFormat,
    FaucetSetupResponse,
    FundFromGasStationParams,

    // OWS signing
    OwsSigningErrorCode,

    // Error constructor options
    RuntimeErrorOptions,
    ValidationErrorArgs,
  } from "@peaqos/peaq-os-sdk";

  // Enums
  import { MachineStatus } from "@peaqos/peaq-os-sdk";
  ```
</Accordion>

***

## Constants

<Accordion title="Event types & trust levels">
  ```typescript theme={"theme":{"light":"github-light-default","dark":"github-dark"}}
  import {
    EVENT_TYPE_REVENUE,        // 0
    EVENT_TYPE_ACTIVITY,       // 1

    TRUST_SELF_REPORTED,       // 0
    TRUST_ON_CHAIN_VERIFIABLE, // 1
    TRUST_HARDWARE_SIGNED,     // 2
  } from "@peaqos/peaq-os-sdk";
  ```
</Accordion>

<Accordion title="DID attributes & limits">
  ```typescript theme={"theme":{"light":"github-light-default","dark":"github-dark"}}
  import {
    DID_ATTR_MACHINE_ID,
    DID_ATTR_NFT_TOKEN_ID,
    DID_ATTR_OPERATOR,
    DID_ATTR_DOCUMENTATION_URL,
    DID_ATTR_DATA_API,
    DID_ATTR_DATA_VISIBILITY,
    DID_ATTR_MACHINES,

    DID_MAX_NAME_BYTES,  // 64
    DID_MAX_VALUE_BYTES, // 2560
  } from "@peaqos/peaq-os-sdk";
  ```
</Accordion>

<Accordion title="Chain IDs & LayerZero endpoints">
  ```typescript theme={"theme":{"light":"github-light-default","dark":"github-dark"}}
  import {
    SUPPORTED_CHAIN_IDS,  // { peaq: 3338, ethereum: 1, base: 8453, polygon: 137, arbitrum: 42161, optimism: 10 }
    LAYER_ZERO_EIDS,      // { peaq: 30302, base: 30184 }
    DEFAULT_API_URL,      // "http://127.0.0.1:8000"
  } from "@peaqos/peaq-os-sdk";
  ```
</Accordion>

<Accordion title="Wallet import chains, key types & passphrase env">
  ```typescript theme={"theme":{"light":"github-light-default","dark":"github-dark"}}
  import {
    IMPORT_CHAIN_EVM,       // "evm" (default)
    IMPORT_CHAIN_SOLANA,    // "solana"
    IMPORT_CHAIN_BITCOIN,   // "bitcoin"
    IMPORT_CHAIN_COSMOS,    // "cosmos"
    IMPORT_CHAIN_TRON,      // "tron"
    IMPORT_CHAIN_TON,       // "ton"
    IMPORT_CHAIN_SUI,       // "sui"
    IMPORT_CHAIN_XRPL,      // "xrpl"
    IMPORT_CHAIN_SPARK,     // "spark"
    IMPORT_CHAIN_FILECOIN,  // "filecoin"

    KEY_TYPE_MNEMONIC,      // "mnemonic"
    KEY_TYPE_PRIVATE_KEY,   // "private_key"

    OWS_PASSPHRASE_ENV,         // "OWS_PASSPHRASE"
    DEFAULT_MCR_HTTP_TIMEOUT_MS, // 30_000
  } from "@peaqos/peaq-os-sdk";
  ```

  `IMPORT_CHAIN_*` is the union behind the `ImportChain` type alias used by `importWallet`. `KEY_TYPE_*` matches the `KeyType` field on `WalletInfo`.
</Accordion>

<Accordion title="OWS signing error codes">
  ```typescript theme={"theme":{"light":"github-light-default","dark":"github-dark"}}
  import {
    OWS_ERROR_WALLET_NOT_FOUND,     // "WALLET_NOT_FOUND"
    OWS_ERROR_INVALID_PASSPHRASE,   // "INVALID_PASSPHRASE"
    OWS_ERROR_INVALID_INPUT,        // "INVALID_INPUT"
    OWS_ERROR_POLICY_DENIED,        // "POLICY_DENIED"
    OWS_ERROR_CHAIN_NOT_SUPPORTED,  // "CHAIN_NOT_SUPPORTED"
  } from "@peaqos/peaq-os-sdk";
  ```

  Surface area for the OWS-native signing path used by `PeaqosClient.fromWallet(..., owsSigning: true)`. The matching `OwsSigningErrorCode` type is the union of all five string-literal codes.
</Accordion>
