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

# Submit events

> Submit revenue and activity events to the EventRegistry. Covers validation, data hashing, event types, cross-chain patterns, and operational limits.

Revenue events (type 0) and activity events (type 1) feed the [Machine Credit Rating](/peaqos/concepts/machine-credit-rating). Every event is validated client-side, hashed, and submitted on-chain to the EventRegistry contract.

## Event types

| Type     | Value | Purpose                                             | Example                                            |
| :------- | :---- | :-------------------------------------------------- | :------------------------------------------------- |
| Revenue  | `0`   | Records economic value generated by the machine     | A claw machine collects \$5.00 from a play session |
| Activity | `1`   | Records operational activity with no direct revenue | A weather sensor reports a telemetry ping          |

## Trust levels

Each event carries a trust level describing how the data was attested. See [Trust levels](/peaqos/concepts/trust-levels) for the concept overview.

| Level               | Value | Meaning                                             | Needs source tx hash? |
| :------------------ | :---- | :-------------------------------------------------- | :-------------------- |
| Self-reported       | `0`   | Machine self-reports. No external verification.     | No                    |
| On-chain verifiable | `1`   | Event references a verifiable on-chain transaction. | Yes                   |
| Hardware-signed     | `2`   | Event signed by tamper-resistant hardware.          | No                    |

## Currency and value units

`currency` is a first-class parameter on `submitEvent` / `submit_event`. Revenue events take a 3-10 char uppercase alphanumeric code (`USD`, `HKD`, `JPY`, …); activity events must pass `""`. The SDK applies a smart default when omitted on single-event submits (revenue → `"USD"`, activity → `""`); `batchSubmitEvents` / `batch_submit_events` requires it explicitly.

`value` is an **ISO 4217 minor-unit integer**:

| Currency                                             | Subunit divisor | Example                  |
| :--------------------------------------------------- | :-------------- | :----------------------- |
| `USD`, `HKD`, `EUR` (and other 2-decimal currencies) | `100`           | `$1.23 → value: 123`     |
| `JPY`, `KRW`, `VND` (no subunits)                    | `1`             | `¥100 → value: 100`      |
| `BHD`, `KWD`, `OMR` (3-decimal)                      | `1000`          | `BD 1.234 → value: 1234` |

The MCR pipeline converts `value` to USD cents using the FX rate at `timestamp`. The converted amount surfaces on [`GET /machine/{did}`](/peaqos/api-reference/get-machine) as `usd_value` (USD cents integer) on revenue events when `data_visibility` is `onchain`. `amount_status` distinguishes `"ok"`, `"unsupported_currency"` (currency not in the FX whitelist), and `"fx_unavailable"` (degraded FX feed). Non-`"ok"` rows score conservatively and surface `mcr_degraded: true` on [`GET /mcr/{did}`](/peaqos/api-reference/get-mcr).

Activity events ignore the FX path entirely. They don't accumulate revenue.

## Validation

Call `validateSubmitEventParams` before submitting. It throws `ValidationError` on the first invalid field.

<CodeGroup>
  ```typescript JS/TS theme={"theme":{"light":"github-light-default","dark":"github-dark"}}
  import {
    validateSubmitEventParams,
    computeDataHash,
    EVENT_TYPE_REVENUE,
    TRUST_SELF_REPORTED,
    SUPPORTED_CHAIN_IDS,
  } from "@peaqos/peaq-os-sdk";

  const params = {
    machineId: 1,
    eventType: EVENT_TYPE_REVENUE,      // 0
    value: 500,                          // $5.00 in cents
    currency: "USD",
    timestamp: Math.floor(Date.now() / 1000) - 10,
    rawData: new TextEncoder().encode(JSON.stringify({ session: "abc123" })),
    trustLevel: TRUST_SELF_REPORTED,     // 0
    sourceChainId: SUPPORTED_CHAIN_IDS.peaq, // 3338
    sourceTxHash: null,
    metadata: new Uint8Array([]),
  };

  // Throws ValidationError if any field is invalid
  validateSubmitEventParams(params);
  ```

  ```python Python theme={"theme":{"light":"github-light-default","dark":"github-dark"}}
  import json
  import time

  from peaq_os_sdk import EVENT_TYPE_REVENUE, TRUST_SELF_REPORTED, SUPPORTED_CHAINS
  from peaq_os_sdk.types.events import SubmitEventParams
  from peaq_os_sdk.validation import validate_submit_event_params

  params = SubmitEventParams(
      machine_id=1,
      event_type=EVENT_TYPE_REVENUE,           # 0
      value=500,                                # $5.00 in cents
      currency="USD",
      timestamp=int(time.time()),
      raw_data=json.dumps({"session": "abc123"}).encode(),
      trust_level=TRUST_SELF_REPORTED,          # 0
      source_chain_id=SUPPORTED_CHAINS["peaq"], # 3338
      source_tx_hash=None,
      metadata=b"",
  )

  # Raises ValidationError if any field is invalid
  validate_submit_event_params(params)
  ```
</CodeGroup>

### Validation rules

| Field                                  | Constraint                                           | Error if violated                                                                     |
| :------------------------------------- | :--------------------------------------------------- | :------------------------------------------------------------------------------------ |
| `machineId` / `machine_id`             | Positive integer                                     | `machineId must be a positive integer`                                                |
| `eventType` / `event_type`             | `0` or `1`                                           | `eventType must be 0 or 1`                                                            |
| `value`                                | Non-negative integer (ISO 4217 minor units)          | `value must be non-negative`                                                          |
| `currency`                             | Revenue: `^[A-Z0-9]{3,10}$`. Activity: must be `""`. | `currency must match ^[A-Z0-9]{3,10}$` / `currency must be empty for activity events` |
| `trustLevel` / `trust_level`           | `0`, `1`, or `2`                                     | `trustLevel must be 0, 1, or 2`                                                       |
| `sourceChainId` / `source_chain_id`    | `0`, `3338`, or `8453`                               | `sourceChainId must be a supported chain ID`                                          |
| `rawData` / `raw_data`                 | Non-empty when provided                              | `rawData must not be empty when provided`                                             |
| `sourceTxHash` / `source_tx_hash`      | 0x-prefixed 32-byte hex (66 chars) when provided     | `sourceTxHash must be a 0x-prefixed 32-byte hex string`                               |
| `timestamp`                            | Positive integer                                     | `timestamp must be a positive integer`                                                |
| `sourceTxHash` when `trustLevel === 1` | Required                                             | `sourceTxHash is required when trustLevel is 1`                                       |

The contract additionally rejects `metadata` larger than 4096 bytes with a `MetadataTooLarge` revert. The SDK validators don't enforce this client-side, so oversized payloads surface as a transaction failure (`RuntimeError`/`RpcError` with `code: "MetadataTooLarge"`) rather than `ValidationError`.

## Computing the data hash

The EventRegistry stores a keccak256 hash of the raw data, not the data itself. Compute it with `computeDataHash` (JS) or `compute_data_hash` (Python).

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

  const rawData = new TextEncoder().encode(
    JSON.stringify({ session: "abc123", amount: 500 })
  );
  const hash = computeDataHash(rawData);
  // hash: "0x9c22ff5f21f0b81b113e63f7db6da94fedef11b2119b4088b89664fb9a3cb658"
  ```

  ```python Python theme={"theme":{"light":"github-light-default","dark":"github-dark"}}
  from peaq_os_sdk.utils import compute_data_hash

  raw_data = b'{"session": "abc123", "amount": 500}'
  data_hash = compute_data_hash(raw_data)
  # data_hash is 32 bytes (keccak256)
  ```
</CodeGroup>

The hash is passed as the `dataHash` field in the on-chain `MachineEvent` struct. Consumers who need to verify the original data compare its keccak256 against the stored hash.

## Submitting a revenue event (type 0)

<CodeGroup>
  ```typescript JS/TS theme={"theme":{"light":"github-light-default","dark":"github-dark"}}
  import "dotenv/config";
  import {
    PeaqosClient,
    validateSubmitEventParams,
    computeDataHash,
    EVENT_TYPE_REVENUE,
    TRUST_SELF_REPORTED,
    SUPPORTED_CHAIN_IDS,
  } from "@peaqos/peaq-os-sdk";

  const client = PeaqosClient.fromEnv();

  const rawData = new TextEncoder().encode(
    JSON.stringify({ session: "abc123", amount: 500 })
  );

  const params = {
    machineId: 1,
    eventType: EVENT_TYPE_REVENUE,
    value: 500,                       // $5.00 in cents
    currency: "USD",
    timestamp: Math.floor(Date.now() / 1000) - 10,
    rawData,
    trustLevel: TRUST_SELF_REPORTED,
    sourceChainId: SUPPORTED_CHAIN_IDS.peaq,
    sourceTxHash: null,
    metadata: new Uint8Array([]),
  };

  validateSubmitEventParams(params);

  const { txHash, dataHash } = await client.submitEvent(params);
  console.log("Submitted revenue event:", { txHash, dataHash });
  ```

  ```python Python theme={"theme":{"light":"github-light-default","dark":"github-dark"}}
  from dotenv import load_dotenv
  import json
  import time
  from peaq_os_sdk import (
      PeaqosClient,
      EVENT_TYPE_REVENUE,
      TRUST_SELF_REPORTED,
      SUPPORTED_CHAINS,
  )
  from peaq_os_sdk.types.events import SubmitEventParams
  from peaq_os_sdk.validation import validate_submit_event_params

  load_dotenv() # load envs from .env file

  client = PeaqosClient.from_env()

  raw_data = json.dumps({"session": "abc123", "amount": 500}).encode()

  params = SubmitEventParams(
      machine_id=1,
      event_type=EVENT_TYPE_REVENUE,
      value=500,                                # $5.00 in cents
      currency="USD",
      timestamp=int(time.time()) - 10,
      raw_data=raw_data,
      trust_level=TRUST_SELF_REPORTED,
      source_chain_id=SUPPORTED_CHAINS["peaq"],
      source_tx_hash=None,
      metadata=b"",
  )

  validate_submit_event_params(params)

  tx_hash, data_hash = client.submit_event(
      machine_id=params.machine_id,
      event_type=params.event_type,
      value=params.value,
      currency=params.currency,
      timestamp=params.timestamp,
      raw_data=params.raw_data,
      trust_level=params.trust_level,
      source_chain_id=params.source_chain_id,
      source_tx_hash=params.source_tx_hash,
      metadata=params.metadata,
  )
  print("Submitted revenue event:", tx_hash, data_hash.hex())
  ```
</CodeGroup>

## Submitting an activity event (type 1)

Activity events record operational telemetry with no direct revenue value.

<CodeGroup>
  ```typescript JS/TS theme={"theme":{"light":"github-light-default","dark":"github-dark"}}
  import "dotenv/config";
  import {
    PeaqosClient,
    validateSubmitEventParams,
    computeDataHash,
    EVENT_TYPE_ACTIVITY,
    TRUST_SELF_REPORTED,
    SUPPORTED_CHAIN_IDS,
  } from "@peaqos/peaq-os-sdk";

  const client = PeaqosClient.fromEnv();

  const rawData = new TextEncoder().encode(
    JSON.stringify({ type: "heartbeat", uptimeSeconds: 86400 })
  );

  const params = {
    machineId: 1,
    eventType: EVENT_TYPE_ACTIVITY, // 1
    value: 0,                       // No revenue
    currency: "",                   // activity events must be empty
    timestamp: Math.floor(Date.now() / 1000) - 10,
    rawData,
    trustLevel: TRUST_SELF_REPORTED,
    sourceChainId: SUPPORTED_CHAIN_IDS.peaq,
    sourceTxHash: null,
    metadata: new Uint8Array([]),
  };

  validateSubmitEventParams(params);

  const { txHash, dataHash } = await client.submitEvent(params);
  console.log("Submitted activity event:", { txHash, dataHash });
  ```

  ```python Python theme={"theme":{"light":"github-light-default","dark":"github-dark"}}
  from dotenv import load_dotenv
  import json
  import time
  from peaq_os_sdk import (
      PeaqosClient,
      EVENT_TYPE_ACTIVITY,
      TRUST_SELF_REPORTED,
      SUPPORTED_CHAINS,
  )
  from peaq_os_sdk.types.events import SubmitEventParams
  from peaq_os_sdk.validation import validate_submit_event_params

  load_dotenv() # load envs from .env file

  client = PeaqosClient.from_env()

  raw_data = json.dumps({"type": "heartbeat", "uptime_seconds": 86400}).encode()

  params = SubmitEventParams(
      machine_id=1,
      event_type=EVENT_TYPE_ACTIVITY,           # 1
      value=0,                                    # No revenue
      currency="",                                # activity events must be empty
      timestamp=int(time.time()) - 10,
      raw_data=raw_data,
      trust_level=TRUST_SELF_REPORTED,
      source_chain_id=SUPPORTED_CHAINS["peaq"],
      source_tx_hash=None,
      metadata=b"",
  )

  validate_submit_event_params(params)

  tx_hash, data_hash = client.submit_event(
      machine_id=params.machine_id,
      event_type=params.event_type,
      value=params.value,
      currency=params.currency,
      timestamp=params.timestamp,
      raw_data=params.raw_data,
      trust_level=params.trust_level,
      source_chain_id=params.source_chain_id,
      source_tx_hash=params.source_tx_hash,
      metadata=params.metadata,
  )
  print("Submitted activity event:", tx_hash, data_hash.hex())
  ```
</CodeGroup>

## Cross-chain revenue pattern

When a machine earns revenue on another chain (e.g., Base), reference the source transaction for on-chain verifiable trust (level 1).

<CodeGroup>
  ```typescript JS/TS 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";

  const params = {
    machineId: 1,
    eventType: EVENT_TYPE_REVENUE,
    value: 1200,                              // $12.00 in cents
    currency: "USD",
    timestamp: Math.floor(Date.now() / 1000) - 10,
    rawData: new TextEncoder().encode(JSON.stringify({ invoice: "INV-0042" })),
    trustLevel: TRUST_ON_CHAIN_VERIFIABLE, // 1
    sourceChainId: SUPPORTED_CHAIN_IDS.base, // 8453
    sourceTxHash: "0xa1b2c3d4e5f6789000000000000000000000000000000000000000000000a1b2",
    metadata: new Uint8Array([]),
  };

  validateSubmitEventParams(params);
  // sourceTxHash is required when trustLevel is 1.
  // The MCR system can verify this transaction on Base.
  ```

  ```python Python theme={"theme":{"light":"github-light-default","dark":"github-dark"}}
  import json
  import time

  from peaq_os_sdk import (
      EVENT_TYPE_REVENUE,
      TRUST_ON_CHAIN_VERIFIABLE,
      SUPPORTED_CHAINS,
  )
  from peaq_os_sdk.types.events import SubmitEventParams
  from peaq_os_sdk.validation import validate_submit_event_params

  params = SubmitEventParams(
      machine_id=1,
      event_type=EVENT_TYPE_REVENUE,
      value=1200,                                    # $12.00 in cents
      currency="USD",
      timestamp=int(time.time()),
      raw_data=json.dumps({"invoice": "INV-0042"}).encode(),
      trust_level=TRUST_ON_CHAIN_VERIFIABLE,        # 1
      source_chain_id=SUPPORTED_CHAINS["base"],     # 8453
      source_tx_hash="0xa1b2c3d4e5f6789000000000000000000000000000000000000000000000a1b2",
      metadata=b"",
  )

  validate_submit_event_params(params)
  # source_tx_hash is required when trust_level is 1.
  # The MCR system can verify this transaction on Base.
  ```
</CodeGroup>

### Supported chain IDs

| Chain                         | ID     |
| :---------------------------- | :----- |
| peaq (same-chain, or use `0`) | `3338` |
| Base                          | `8453` |

## Operational limits

The SDK enforces per-transaction value caps and rate limits before submitting.

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

  checkOperationalLimits(
    { machineId: 1, value: 500 },
    {
      maxValuePerTx: 10000,
      rateLimitMaxEvents: 60,
      rateLimitWindowSeconds: 3600,
    },
    tracker, // EventTracker from previous submissions, or null
  );
  // Throws ValueCapExceeded if value > maxValuePerTx
  // Throws RateLimitExceeded if count >= rateLimitMaxEvents within window
  ```

  ```python Python theme={"theme":{"light":"github-light-default","dark":"github-dark"}}
  from peaq_os_sdk.types.client import OperationalLimits
  from peaq_os_sdk.validation import check_operational_limits

  check_operational_limits(
      params,  # SubmitEventParams (or any object with .machine_id and .value)
      OperationalLimits(
          max_value_per_tx=10000,
          rate_limit_max_events=60,
          rate_limit_window_seconds=3600,
      ),
      tracker,  # EventTracker from previous submissions, or None
  )
  # Raises ValueCapExceeded if value > max_value_per_tx
  # Raises RateLimitExceeded if count >= rate_limit_max_events within window
  ```
</CodeGroup>

| Limit                                          | Error type          | Description                                            |
| :--------------------------------------------- | :------------------ | :----------------------------------------------------- |
| `maxValuePerTx` / `max_value_per_tx`           | `ValueCapExceeded`  | Single event value exceeds the per-transaction cap     |
| `rateLimitMaxEvents` / `rate_limit_max_events` | `RateLimitExceeded` | Too many events submitted within the rate-limit window |

Set limits to `0` to disable (the default).

## Error handling

`submitEvent` / `submit_event` raise four distinct error types. Validation and limit errors are local; `RuntimeError` (JS) / `RpcError` (Python) wraps every chain or RPC failure. JS collapses chain and HTTP errors into a single `RuntimeError`; Python keeps them separate (`RpcError` for chain, `ApiError` for HTTP).

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

  try {
    const { txHash, dataHash } = await client.submitEvent(params);
  } catch (err) {
    if (err instanceof ValidationError) {
      // Bad params: check err.field, err.constraint
    } else if (err instanceof ValueCapExceeded) {
      // Per-tx value cap tripped
    } else if (err instanceof RateLimitExceeded) {
      // Local rate limit tripped
    } else if (err instanceof RuntimeError) {
      // Chain/RPC failure: err.code carries the contract revert name
      // (e.g. "MetadataTooLarge", "MachineNotFound", "NotAuthorizedSubmitter")
      // or "TX_REVERTED" for unrecognized reverts
    } else {
      throw err;
    }
  }
  ```

  ```python Python theme={"theme":{"light":"github-light-default","dark":"github-dark"}}
  from peaq_os_sdk import (
      ValidationError,
      ValueCapExceeded,
      RateLimitExceeded,
      RpcError,
  )

  try:
      tx_hash, data_hash = client.submit_event(
          machine_id=params.machine_id,
          event_type=params.event_type,
          value=params.value,
          currency=params.currency,
          timestamp=params.timestamp,
          raw_data=params.raw_data,
          trust_level=params.trust_level,
          source_chain_id=params.source_chain_id,
          source_tx_hash=params.source_tx_hash,
          metadata=params.metadata,
      )
  except ValidationError as err:
      # Bad params: inspect err.field, err.constraint
      raise
  except ValueCapExceeded:
      # Per-tx value cap tripped
      raise
  except RateLimitExceeded:
      # Local rate limit tripped
      raise
  except RpcError as err:
      # Chain/RPC failure: err.code carries the contract revert name
      # (e.g. "MetadataTooLarge", "MachineNotFound") or "TX_REVERTED"
      raise
  ```
</CodeGroup>

See [SDK errors reference](/peaqos/sdk-reference/errors) for the full code map and the cross-language equivalence between `RuntimeError` (JS) and `RpcError`/`ApiError` (Python).

## Next steps

* [Events concept](/peaqos/concepts/events) for deeper coverage of event types and trust levels
* [SDK reference](/peaqos/sdk-reference/sdk-js) for full method signatures
