Public API

The public API serves the Sealroom registry, indexed from the chain, and builds the transactions your wallet signs. It never holds keys or submits transactions for you.

Base URL

<api-url>

All routes are under /v1, except /health. Requests and responses are JSON. The contract version is 1.0.0, reported in every response.

MethodRouteReturns
GET/healthService status
GET/v1/parametersThe deployment's protocol parameters
GET/v1/summaryRegistry totals
GET/v1/{resource}A page of a resource
GET/v1/{resource}/{id}One record
GET/v1/eligibility?modelId=…&nodeId=…Whether a node may serve a model, rule by rule
POST/v1/transactions/previewAn unsigned transaction for a wallet intent
POST/v1/applicationsAn operator or model-owner application

Response envelope

Every response has the same shape:

{ "ok": true, "data": { }, "meta": { } }
{ "ok": false, "error": { "code": "NOT_FOUND", "message": "Resource not found.", "retryable": false }, "meta": { } }

meta describes the read:

FieldMeaning
contractVersion1.0.0
requestIdThe request's id, also sent as the X-Request-Id header
asOfServer time of the response
index.statusfresh while the index is within 30 seconds and 150 slots of the chain, otherwise stale
index.indexedSlot, index.chainSlot, index.lagSlotsThe slot indexed, the chain's slot and the difference
index.indexedAtWhen the index last caught up
index.chainTimeThe chain clock used for expiries and timelocks

Formats

KindFormat
Accounts and idsBase58 public keys (32 bytes)
USDC amounts, slots, countersDecimal strings of integers. Amounts are base units with 6 decimals: "25000" is 0.025 USDC
Hashes64 lowercase hex characters (SHA-256)
Runtime measurements96 lowercase hex characters (48 bytes)
SharesIntegers in basis points, 0 to 10,000
TimesISO 8601 UTC timestamps
Endpoints and broker URLsHTTPS, at most 128 bytes

Use big-integer arithmetic for amounts.

Health

GET /health returns status (ok), mode (live), contractVersion, database and chain (connected or degraded).

Parameters

GET /v1/parameters returns the settings the program and services run with. Single settings have the form { "status": "set", "value": …, "source": "chain" }.

FieldMeaning
protocolFeeBpsProtocol fee, 500 bps at launch
pendingProtocolFeeA proposed fee change and its effectiveAt, or null
attestationTtlSecondsAttestation lifetime: 86,400 (24 hours)
verifierSetmembers, threshold (3), expectedMembers (5), epoch
unbondingPeriodSeconds2,592,000 (30 days)
splitDelaySeconds604,800 (7 days)
runtimeAllowlistTimelockSeconds172,800 (48 hours)
limitssplitEntries 8, allowedRuntimes 8, operatorAllowlist 16, verifierKeys 7, endpointBytes 128, gpusPerNode 8
usdcMint, usdcDecimalsThe USDC mint and 6
programId, clusterThe sealroom program and the Solana cluster
pausedWhether the protocol is paused
adminMultisig, arbitrationMultisigThe admin and arbitration authorities
protocolFeeRecipientAtaThe treasury's USDC account
bondMinimumsByTierMinimum bond for tiers 1, 2 and 3
disputeDepositThe dispute deposit
operatorShareDefaultBpsThe suggested operator share for new models
settlementPoliciesclaims, splitSettlement, disputeDeposit, claimDestination
disputeConfigurationOpen-dispute limits, forfeiture beneficiary, pending changes
paymentsWhich call schemes the deployment quotes (exact, upto) and the payment-channels program

Summary

GET /v1/summary returns operators, attestedNodes, models, openLanes, totalDistributed (base units) and openDisputes.

Resources

ResourceRecordFilters
operatorsAn operator: authority, tier, status, bondAmount, unbondAt, frozen, payoutAta, platformIds, nodeIds, disputeIds, nextDisputeSequence, openDisputeCount, revenue, uptimestatus, tier, operatorId
platformsA server: operatorId, platformIdHash, identityKind, gpuUeidHashes, nodeIdsoperatorId
nodesA node: operatorId, platformId, index, nodeKey, endpoint, gpuModel, gpuCount, status, currentAttestationId, attestationExpiresAtstatus, operatorId, platformId, nodeId
attestationsAn attestation: revision, nodeId, runtimeMeasurement, gpuRimHash, evidenceHash, enclaveKeyHash, tlsPublicKeyHash, voucherPublicKey, tcbStatus, attestedAt, expiresAt, method, signingVerifiers, statusstatus, operatorId, platformId, nodeId, method
runtimesA runtime image: measurement, imageName, imageVersion, sourceCommit, status, activatesAtstatus
modelsA model: modelId, owner, manifestHash, keyBroker, policy, price, derivativeRoyaltyBps, baseModelId, split, pendingSplit, eligibleNodeIds, revenueowner, modelId
lanesA lane: modelId, nodeId, operatorId, vault, status, vaultBalance, totalIn, totalDistributed, activeSplit, claimable, admission creditstatus, modelId, nodeId, laneId, operatorId
distributionsA distribution: laneId, distributedAt, grossAmount, protocolFeeBps, payouts, splitHashlaneId, modelId, nodeId
disputesA dispute: operatorId, modelId, claimant, evidenceHash, deposit, status, rulingstatus, operatorId, modelId, claimant
key-releasesA key broker decision: modelId, nodeId, attestationId, attestationRevision, outcome, reasonCodes, releasedAtoperatorId, nodeId, modelId, outcome
activityA registry event: kind, occurredAt, source, entityId, amount, signature, summaryoperatorId, nodeId, modelId, kind

Every record from the chain has an id (its account address) and a chain reference with the slot, transaction signature and observedAt time of its latest change.

On a resource's own list, its id filter matches the record id: GET /v1/models?modelId=<account> returns that model. Filters are exact matches, and a filter a resource does not support returns VALIDATION_ERROR. Each list also accepts q, a case-insensitive search over the record's name, endpoint, image name, summary or id.

Status values:

Resourcestatus
operatorsbonded, unbonding, frozen, unbonded
nodesregistered, serving, offline, disabled
attestationsvalid, expiring_soon, expired, revoked
runtimespending, allowed, revoked
lanesopen, closed
disputesopen, ruled_for_claimant, dismissed

GET /v1/attestations/{id} returns the latest revision of that attestation. activity kinds include model_created, lane_opened, lane_payment, distribution, attestation_recorded, attestation_revoked, attestation_expired, key_released, key_denied, bond_deposited, unbond_requested, split_proposed, split_applied, dispute_opened, dispute_ruled and claim_paid, among others.

Pagination

Lists return a page:

{ "items": [ ], "nextCursor": "eyJvZmZzZXQiOjIw…", "total": 57 }
ParameterMeaning
limit1 to 100 items, 20 by default
cursorThe nextCursor of the previous page

nextCursor is null on the last page. A cursor belongs to its resource and filters: keep the same query and change only cursor.

curl "<api-url>/v1/lanes?modelId=<model>&status=open&limit=50"

Eligibility

GET /v1/eligibility?modelId=<model>&nodeId=<node> returns eligible, checkedAt, the attestationId and attestationRevision it used, reasons (the codes that failed) and checks, one per rule:

{ "code": "bond_below_min", "passed": true, "message": "Operator bond must meet the model and protocol tier minimums.", "actual": "1000000000", "required": "100000000" }

The rule codes are no_valid_attestation, attestation_expired, attestation_revoked, runtime_not_allowed, platform_not_registered, tier_below_min, bond_below_min, operator_frozen, operator_not_on_allowlist, tcb_not_up_to_date, node_disabled and protocol_paused.

Transaction previews

POST /v1/transactions/preview takes one wallet intent and returns the exact transaction to sign. Every intent has an action and an authority: the wallet that signs and pays the network fee.

Intents

actionInputs besides authorityWho sends it
create_modelmodelId (optional), manifestHash, keyBroker { publicKey, url }, policy { allowedRuntimes, minTier, minBond, operatorAllowlist }, price, derivativeRoyaltyBps, baseModelId (or null), splitOwner
open_lanemodelId, nodeIdAnyone
propose_splitmodelId, split, consentSigners (optional)Owner
apply_splitmodelIdAnyone, after 7 days
cancel_splitmodelIdOwner
distributelaneIdAnyone
prepare_claimlaneId, recipientAta, recipientOwnerAnyone
claimlaneId, recipientAta, destinationAta (optional)The recipient
deposit_bondamount (greater than 0)Operator
request_unbondnoneOperator
cancel_unbondnoneOperator
withdraw_bondnoneOperator, after 30 days
register_platformplatformIdHash, gpuUeidHashes (1 to 8, distinct)Operator, co-signed by the admin multisig
register_nodeplatformId, index (0 to 65,535), nodeKey, endpoint, gpuModel (H100 or H200), gpuCount (1 to 8), gpuUeidHashes (exactly gpuCount)Operator
open_disputeoperatorId, modelId, evidenceHash, disputeNonce, laneId (optional)Model owner
claim_disputedisputeId, destinationAta (optional)The claimant
claim_forfeited_depositdisputeId, destinationAta (optional)The forfeiture beneficiary

price is { "scheme": "exact", "perCall": "25000" } or { "scheme": "upto", "inputPerMillionTokens": "…", "outputPerMillionTokens": "…", "maxPerCall": "…" }. A split is 1 to 8 entries { recipientAta, recipientOwner, bps, locked, role } with distinct canonical USDC accounts, summing to 10,000 bps. role is owner, operator, base_model or contributor. Include recipientOwner for any recipient whose USDC account does not exist yet: the transaction creates it.

curl -X POST "<api-url>/v1/transactions/preview" \
  -H "Content-Type: application/json" \
  -d '{"action":"distribute","authority":"<your wallet>","laneId":"<lane>"}'

Preview response

FieldMeaning
submittabletrue when your signature completes the transaction
intentThe intent as resolved, for example with the modelId the API picked
serializedTransactionThe transaction, base64. Deserialize it, sign it without changing the message and send it to Solana
requiredSignersEvery signer the transaction needs
estimatedFeeLamportsThe network fee
instructionsEach instruction's name, programId and accounts
blockedByCodes that prevent the action; empty when it can go ahead
messageA sentence explaining the result

When blockedBy is not empty, serializedTransaction is null and message says what to do.

Preview blockers

CodeMeaning
PROTOCOL_PAUSEDThe protocol is paused
MODEL_ALREADY_EXISTSThis owner already used that modelId
MODEL_OWNER_REQUIREDOnly the model's owner can change its split
PENDING_SPLIT_EXISTSApply or cancel the pending split before proposing another
NO_PENDING_SPLITThe model has no pending split
SPLIT_TIMELOCK_PENDINGThe split can be applied after its effectiveAt time
OPERATOR_LOCK_UNSUPPORTEDOperator entries cannot be locked
RECIPIENT_OWNER_REQUIREDAdd recipientOwner for a recipient whose USDC account does not exist yet
CANONICAL_RECIPIENT_REQUIREDSplit recipients use their canonical USDC accounts
RECIPIENT_CONSENT_SIGNATURE_REQUIREDLowering a locked entry needs that recipient's signature
CLAIM_PREPARATION_REQUIREDSend prepare_claim for the listed recipients, then distribute
INVALID_LANE_CLAIMAn existing claim names a different recipient
CLAIM_BENEFICIARY_REQUIREDOnly the recorded beneficiary can claim these funds
DESTINATION_ATA_REQUIREDA USDC account that does not exist yet must be your canonical one
INVALID_TOKEN_ACCOUNTChoose a USDC account owned by the recipient
TOKEN_ACCOUNT_FROZENThat USDC account is frozen
OPERATOR_FROZENThe operator is frozen
UNBONDING_ACTIVECancel unbonding before adding to the bond
BOND_REQUIREDDeposit a bond before requesting unbonding
UNBOND_NOT_REQUESTEDRequest unbonding first
UNBOND_TIMELOCK_PENDINGThe bond can be withdrawn after its unbondAt time
OPEN_DISPUTEWait for the operator's open disputes to be ruled
ADMIN_SIGNATURE_REQUIREDThe transaction still needs the admin multisig's co-signature
GPU_UEIDS_REQUIREDName the exact registered GPU UEID hashes the node claims
GPU_NOT_REGISTEREDA selected GPU is not registered on this platform
GPU_ALREADY_CLAIMEDA selected GPU already belongs to another node
RELATED_LANE_REQUIREDChoose a lane of your model served by this operator
INSUFFICIENT_PAID_USAGEThe lane needs unused paid protocol fees at least equal to the deposit
DISPUTE_OPENFunds can be claimed after the ruling
DISPUTE_NOT_DISMISSEDOnly a dismissed dispute has a forfeited deposit
TRANSACTION_TOO_LARGEThe transaction exceeds 1,232 bytes, for example a split with many new recipient accounts. Create those accounts first
FEE_ESTIMATE_UNAVAILABLE, CHAIN_CLOCK_UNAVAILABLE, CHAIN_STATE_UNAVAILABLE, MODEL_ID_UNAVAILABLERequest the preview again

Applications

POST /v1/applications receives operator and model-owner applications from the form at https://sealroom.cloud/apply. It needs an Idempotency-Key header (16 to 128 letters, digits, _ or -) and the form's anti-abuse token. Sending the same key and body again returns the same receipt: id, status (received), confirmation and receivedAt.

Errors

HTTPcodeMeaning
401UNAUTHORIZEDThe route needs authentication
403FORBIDDENThe request's origin is not allowed
403ABUSE_REJECTEDThe anti-abuse check failed
404NOT_FOUNDNo such record or route
405METHOD_NOT_ALLOWEDWrong method; the Allow header lists the right one
409IDEMPOTENCY_CONFLICTThe idempotency key was used with a different body
413PAYLOAD_TOO_LARGEThe body exceeds 16 KiB
415UNSUPPORTED_MEDIA_TYPESend Content-Type: application/json
422VALIDATION_ERRORA field is invalid; fieldErrors maps each field to messages
429RATE_LIMITEDWait retryAfterSeconds, also sent as Retry-After
503CONFIGURATION_UNSETA required setting is missing; configurationKeys names it
503INTERNAL_ERRORA backend dependency did not answer; retry

Every error has retryable, which is true for 429 and 5xx responses. The TypeScript client adds NETWORK_ERROR, TIMEOUT, CANCELLED and INVALID_RESPONSE for failures before a valid response arrives.

Rate limits

POST /v1/transactions/preview and POST /v1/applications share a limit of 20 requests per minute for each client address, counted in one-minute windows. A request over the limit returns 429 RATE_LIMITED with Retry-After. Reads are not rate limited.

Headers

HeaderDirectionUse
Content-Type: application/jsonRequestRequired on POST
Idempotency-KeyRequestRequired on POST /v1/applications
OriginRequestBrowser requests are accepted from the Sealroom site's origin. Servers, scripts and agents send none
X-Request-IdResponseThe request's id, equal to meta.requestId
Cache-Control: no-storeResponseEvery response is fresh
Retry-AfterResponseSeconds to wait after a 429
AllowResponsePermitted methods after a 405

Query parameters appear once each; a repeated parameter returns VALIDATION_ERROR.