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.
Revenue events (type 0) and activity events (type 1) feed the 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 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} 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
| 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).
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 chain (e.g., Base), 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
| 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.
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
| 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).
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