Every request that enters the Pura protocol goes through five standard objects. Each one has a JSON Schema in the SDK (sdk/schemas/) and a TypeScript type in @puraxyz/sdk.
JobIntent → CapacityAttestation → VerificationReceipt → PriceSignal → SettlementReceiptA source publishes a JobIntent to request work from the network. This maps to a NIP-90 DVM request event (kinds 5000–5999).
{
"id": "abc123",
"sourceId": "npub1...",
"taskTypeId": "0xabc...",
"payload": { "prompt": "translate this" },
"maxFeeMsat": 5000,
"createdAt": 1700000000
}| Field | Type | Description |
|---|---|---|
| id | string | Unique job identifier |
| sourceId | string | Nostr pubkey of the requesting source |
| taskTypeId | string | keccak256 hash of the NIP-90 kind (e.g. nip90:5000) |
| payload | object | Arbitrary request data for the DVM |
| maxFeeMsat | integer | Maximum fee the source is willing to pay, in millisatoshis |
| createdAt | integer | Unix timestamp |
The SDK type:
import type { JobIntent } from "@puraxyz/sdk";After the CapacityRegistry registers a sink's capacity, the system emits a CapacityAttestation. This is the signed proof that a sink has available throughput.
{
"sinkId": "0xdef...",
"taskTypeId": "0xabc...",
"rawCapacity": 100,
"ewmaSmoothed": 84.3,
"utilization": 0.72,
"epoch": 42,
"signature": "0x..."
}The EWMA smoothing formula ( on-chain default):
Where is the raw capacity reading and is the previous smoothed value. This dampens flash capacity spikes and prevents gaming.
import type { CapacityAttestationWithMetrics } from "@puraxyz/sdk";When a sink completes a job and the result is dual-signed (by both the sink and a verification oracle), the DVMCompletionVerifier emits a VerificationReceipt.
{
"jobId": "abc123",
"sinkId": "0xdef...",
"taskTypeId": "0xabc...",
"resultHash": "0x789...",
"sinkSignature": "0x...",
"oracleSignature": "0x...",
"latencyMs": 245,
"verifiedAt": 1700000300
}Dual signatures prevent unilateral claims. The sink signs the result, then an independent oracle validates and countersigns. Both EIP-712 typed data signatures must verify on-chain before the CompletionTracker records the completion.
import type { VerificationReceipt } from "@puraxyz/sdk";The PricingCurve contract emits a PriceSignal whenever congestion pricing adjusts fees. This is broadcast as a Kind 1090 Nostr event for network-wide visibility.
{
"taskTypeId": "0xabc...",
"baseFee": 1000,
"congestionMultiplier": 1.35,
"effectivePrice": 1350,
"epoch": 42,
"publishedAt": 1700000200
}The congestion pricing formula:
Where is the congestion sensitivity parameter. When load exceeds capacity, prices rise smoothly. When load drops, prices fall back toward the base fee.
import type { PriceSignal } from "@puraxyz/sdk";After a verified completion, settlement happens through one of three rails. The SettlementReceipt records the payment.
{
"jobId": "abc123",
"sinkId": "0xdef...",
"amountMsat": 1350,
"rail": "lightning",
"proof": "preimage:abc...",
"settledAt": 1700000400
}Three settlement rails:
| Rail | Adapter | Mechanism |
|---|---|---|
| lightning | LightningSettlementAdapter | HTLC with sha256 preimage, 86400s timeout |
| superfluid | SuperfluidSettlementAdapter | GDA stream, linear accrual per second |
| direct | DirectSettlementAdapter | ERC-20 transfer from escrow |
import type { SettlementReceipt, SettlementRail } from "@puraxyz/sdk";All five schemas are available as JSON Schema draft-07 files for runtime validation:
import jobSchema from "@puraxyz/sdk/schemas/job-intent.json";
import capacitySchema from "@puraxyz/sdk/schemas/capacity-attestation.json";
import verificationSchema from "@puraxyz/sdk/schemas/verification-receipt.json";
import priceSchema from "@puraxyz/sdk/schemas/price-signal.json";
import settlementSchema from "@puraxyz/sdk/schemas/settlement-receipt.json";Use with any JSON Schema validator (Ajv, Zod schema adapters, etc.) to validate messages at service boundaries.