Skip to main content
Revenue events (type 0) and activity events (type 1) feed the Machine Credit Rating. Every is validated client-side, hashed, and submitted to the EventRegistry contract.

Event types

TypeValuePurposeExample
Revenue0Records economic value generated by the machineA claw machine collects $5.00 from a play session
Activity1Records operational activity with no direct revenueA weather sensor reports a telemetry ping

Trust levels

Each event carries a describing how the data was attested. See Trust levels for the concept overview.
LevelValueMeaningNeeds source tx hash?
Self-reported0Machine self-reports. No external verification.No
On-chain verifiable1Event references a verifiable on-chain transaction.Yes
Hardware-signed2Event 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:
CurrencySubunit divisorExample
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)1000BD 1.234 → value: 1234
The pipeline converts value to USD cents using the FX rate at timestamp. The converted amount surfaces on GET /machine/{did} 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}. 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.
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);

Validation rules

FieldConstraintError if violated
machineId / machine_idPositive integermachineId must be a positive integer
eventType / event_type0 or 1eventType must be 0 or 1
valueNon-negative integer (ISO 4217 minor units)value must be non-negative
currencyRevenue: ^[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_level0, 1, or 2trustLevel must be 0, 1, or 2
sourceChainId / source_chain_id0, 3338, or 8453sourceChainId must be a supported chain ID
rawData / raw_dataNon-empty when providedrawData must not be empty when provided
sourceTxHash / source_tx_hash0x-prefixed 32-byte hex (66 chars) when providedsourceTxHash must be a 0x-prefixed 32-byte hex string
timestampPositive integertimestamp must be a positive integer
sourceTxHash when trustLevel === 1RequiredsourceTxHash is required when trustLevel is 1
The contract additionally rejects metadata larger than 4096 bytes with a MetadataTooLarge . The SDK validators don’t enforce this client-side, so oversized payloads surface as a failure (RuntimeError/RpcError with code: "MetadataTooLarge") rather than ValidationError.

Computing the data hash

The EventRegistry stores a of the raw data, not the data itself. Compute it with computeDataHash (JS) or compute_data_hash (Python).
import { computeDataHash } from "@peaqos/peaq-os-sdk";

const rawData = new TextEncoder().encode(
  JSON.stringify({ session: "abc123", amount: 500 })
);
const hash = computeDataHash(rawData);
// hash: "0x9c22ff5f21f0b81b113e63f7db6da94fedef11b2119b4088b89664fb9a3cb658"
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)

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 });

Submitting an activity event (type 1)

Activity events record operational telemetry with no direct revenue value.
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 });

Cross-chain revenue pattern

When a machine earns revenue on another (e.g., ), reference the source transaction for on-chain verifiable trust (level 1).
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.

Supported chain IDs

ChainID
peaq (same-chain, or use 0)3338
Base8453

Operational limits

The SDK enforces per-transaction value caps and rate limits before submitting.
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
LimitError typeDescription
maxValuePerTx / max_value_per_txValueCapExceededSingle event value exceeds the per-transaction cap
rateLimitMaxEvents / rate_limit_max_eventsRateLimitExceededToo 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).
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;
  }
}
See SDK errors reference for the full code map and the cross-language equivalence between RuntimeError (JS) and RpcError/ApiError (Python).

Next steps