Request Lifecycle
This page walks through how a single Pay‑Per‑Call (PPC) request moves from funding → consensus → settlement. Subscription flows are noted where they differ.
Terminology used here:
apiId(bytes32),requestHash(bytes32),expiresAtMs(uint64). Event names are listed to help you trace on explorers and in your app logs.
0) Pre‑flight (client)
- Fetch descriptor via
AccessRegistry.descriptorOf(apiId)→{ uri, contentHash, updatedAt, version }. - Build your request payload (path/params/headers/body). Compute a deterministic
requestHash(e.g., keccak256 of canonical JSON). - Pick an expiry:
expiresAtMs = nowMs + ΔwhereΔ ≤ maxRequestExpiryMsfrom AccessRegistry. - Ensure the wallet has SYL and the Escrow contract has allowance.
1) Lock funds (PPC)
Caller: Consumer → APIEscrow.lockForCall(apiId, requestHash, expiresAtMs)
What happens on‑chain:
- Price & plan check (
AccessRegistry.apiPlan) → must be PayPerCall and active. - Pull funds from the consumer (SYL =
price). - Create requestId in AccessRegistry (increments
consumerNonce) and emit:AccessRegistry.RequestCreated(containsrequestId,apiId,consumer,requestHash,expiresAtMs,nonce).
- Register with Consensus →
APIConsensus.registerRequest(...)emits:APIConsensus.RequestRegistered(mirrors metadata above).
- Escrow emits:
APIEscrow.Locked(requestId,apiId,consumer,price,expiresAtMs).
Subscriptions: The consumer purchases once via
APIEscrow.purchaseSubscription(apiId). Escrow allocates fees and AccessRegistry records the period. Individual calls don’t lock funds; clients may (optionally) record usage withAccessRegistry.registerSubRequest.
2) Nodes fetch & attest (off‑chain + on‑chain)
Off‑chain: Registered Nodes retrieve the Provider’s response per the descriptor. Providers sign snapshots using EIP‑712.
On‑chain (per Node): APIConsensus.submitSnapshot(requestId, Snapshot, providerSig, pointerURI)
- Snapshot fields:
{ apiId, seqNo, providerTs, ttl, contentHash }. - Checks performed:
- API is active; a providerSigner exists for
apiId. - EIP‑712 signature matches
providerSigner. - Freshness:
providerTswithinmaxSkewMs; ifttl != 0, not stale (capped bymaxTtlMs). - One vote per node; votes tally by
(msgHash → votes). - Provider equivocation is detected: if two different
contentHashare seen for the same(apiId, seqNo), emitProviderEquivocation.
- API is active; a providerSigner exists for
- Emits:
APIConsensus.ResponseSubmittedper snapshot.
Fork‑choice: the candidate with the most votes becomes
topHash. IfseqMonotonicis enabled for the API, finalization also requiresseqNonot regress.
3) Finalization (consensus)
Consensus can finalize early when quorum is reached, or later after expiry + a grace window.
- Early path: Once any digest reaches
quorum, consensus finalizes immediately. - Deadline path: Anyone may call
APIConsensus.finalize(requestId)whennowMs ≥ expiresAtMsandnowMs ≤ expiresAtMs + requestExpiryGraceMs. - Outcomes:
RequestFinalized(success) with{ apiId, seqNo, providerTs, contentHash, msgHash, votes }.RequestFailedwithreason:1 = NoQuorum2 = InactiveAPI
4) Settlement (escrow)
Consensus notifies escrow in both cases (idempotent calls):
- On success →
APIEscrow.settleSuccess(requestId)- Escrow marks the request settled and splits funds per BPS snapshot taken at lock time: Provider / NodePool / Platform.
- Emits:
APIEscrow.Settled.
- On failure →
APIEscrow.settleFailure(requestId, reason)- Escrow refunds the consumer.
- Emits:
APIEscrow.Refunded.
Recipients withdraw accumulated balances via APIEscrow.withdraw() → APIEscrow.Withdrawn.
Node slashing & rewards: During finalization, mismatching Nodes may be slashed (via NodeRegistry). Slashed funds can be redistributed to honest participants, and reputation is increased for honest Nodes.
Sequence (PPC)
Status model (conceptual)
Subscription differences
- Purchase once →
APIEscrow.purchaseSubscription(apiId)allocates fees immediately and records[startTs, endTs]in AccessRegistry. - Per‑call usage under a subscription does not lock funds. Your app may emit
AccessRegistry.registerSubRequest(apiId, requestHash)for accounting/analytics. - Consensus flow for subscription calls can be added by Providers/Nodes off‑chain; on‑chain settlement is not per call in the base flow.
Operational tips
- Choose
expiresAtMsconservatively; allow enough time for nodes to reach quorum but keep it withinmaxRequestExpiryMs. - Watch
RequestRegistered,ResponseSubmitted,RequestFinalized/Failed, andSettled/Refundedto drive UI state. - Idempotency: settlement functions are safe to call again; your indexer can retry without double‑spending.
See also
- Consensus (scoring, quorum, EIP‑712 details)
- Data Integrity (hashing, content addressing)
- Events (full reference)
- Node & Provider guides (ops & best practices)