Every EVM address you send (consumerAddress, owner, address, decrypterAddress,
dataNftAddress, publisherAddress, consumerAddrs, additionalViewers) is accepted in any
casing — checksummed (EIP-55), all-lowercase, all-uppercase — and canonicalized to its checksummed
form by the node before it is used as a lookup key or compared against an owner. A lowercased
address therefore matches the same jobs, services and buckets as the checksummed one.
Signatures are unaffected: the node verifies the signed message against the casing you actually signed, so clients that build the message from a lowercase address keep working.
returns ddo state
| name | type | required | description |
|---|---|---|---|
| did | object | document id or did | |
| chainId | object | chain id of network on which document is provided | |
| nft | object | one or more field names that should be queried against |
123
returns search result for query
| name | type | required | description |
|---|---|---|---|
| q | object | v | text to search for in database |
| query_by | object | v | one or more field names that should be queried against |
{
"q": "0x123",
"query_by": "nftAddress"
}[{
"facet_counts": [],
"found": 1,
"out_of": 1,
"page": 1,
"request_params": {
"collection_name": "ddo",
"per_page": 10,
"q": "0x123"
},
"search_time_ms": 1,
"hits": [
{
"highlights": [
{
"field": "nftAddress",
"snippet": "<mark>0x123</mark>",
"matched_tokens": ["0x123"]
}
],
"document": {
"@context": ["https://w3id.org/did/v1"],
"id": "did:op:ACce67694eD2848dd683c651Dab7Af823b7dd123",
"version": "4.1.0",
"chainId": 1,
"nftAddress": "0x123",
...
},
"text_match": 130916
}
]
}]
returns metadata of document by id
{
"created": "2020-11-15T12:27:48Z",
"updated": "2021-05-17T21:58:02Z",
"description": "Sample description",
"name": "Sample asset",
"type": "dataset",
"author": "OPF",
"license": "https://market.oceanprotocol.com/terms"
}
returns document by id
{
"@context": ["https://w3id.org/did/v1"],
"id": "did:op:ACce67694eD2848dd683c651Dab7Af823b7dd123",
"version": "4.1.0",
"chainId": 1,
"nftAddress": "0x123",
...
}
returns stored nonce for an address
| name | type | required | description |
|---|---|---|---|
| userAddress | string | v | address |
123
returns amount of tokens to transfer to the provider account
| name | type | required | description |
|---|---|---|---|
| documentId | string | v | document id or did |
| serviceId | string | v | id of the service the datatoken is attached to |
| consumerAddress | string | v | consumer address |
{
"datatoken": "0x123",
"nonce": "123",
"providerFee": {
providerFeeAddress: "0x123",
providerFeeToken: "0x123",
providerFeeAmount: 123,
providerData: "0x123",
v: 123,
r: "0x123",
s: "0x123",
validUntil: 123
}
}
returns encrypted blob
| name | type | required | description |
|---|---|---|---|
| nonce | string | v | is required to verify a request paired with a signature |
| consumerAddress | string | v | consumer address |
| signature | string | v | signed message based on nonce |
string
0x123
returns encrypted file
| name | type | required | description |
|---|---|---|---|
| nonce | string | v | is required to verify a request paired with a signature |
| consumerAddress | string | v | consumer address |
| signature | string | v | signed message based on nonce |
if Content-Type = 'application/json'
BaseFileObject
if Content-Type = 'application/octet-stream' || 'multipart/form-data'
FileContent(bytes)
0x123
returns decrypted document
| name | type | required | description |
|---|---|---|---|
| decrypterAddress | string | v | decrypter address |
| chainId | number | v | chain id of network on which document is provided |
| transactionId | string | tx id of encrypted document | |
| dataNftAddress | string | address of nft token | |
| encryptedDocument | string | encrypted document | |
| flags | number | metadata flags if DDO is ECIES encrypted or lzma compressed | |
| documentHash | string | hash based on sha256 of asset | |
| nonce | string | v | is required to verify a request paired with a signature |
| signature | string | v | signed message based on transactionId + dataNftAddress + decrypterAddress + chainId + nonce |
{
"decrypterAddress": "0x123",
"chainId": 123,
"transactionId": "0x123",
"dataNftAddress": "0x123",
"encryptedDocument": "0x123",
"flags": 1,
"documentHash": "0x123",
"nonce": "123",
"signature": "0x123"
}{
"@context": ["https://w3id.org/did/v1"],
"id": "did:op:ACce67694eD2848dd683c651Dab7Af823b7dd123",
"version": "4.1.0",
"chainId": 1,
"nftAddress": "0x123",
...
}
returns file information
| name | type | required | description |
|---|---|---|---|
| type | string | type of storage url or arweave or ipfs |
|
| did | string | document id or did | |
| serviceId | number | service id of services list | |
| fileIndex | number | file index in files array | |
| file | object | file data | |
| checksum | number | index in transaction events list |
{
"type": "123",
"did": "did:op:123",
"serviceId": 123,
"fileIndex": 123,
"file": 123,
"checksum": 123
}[
{
valid: true
contentLength: "123"
contentType: "123"
name: "123"
type: "url or arweave or ipfs"
}
]
returns a file stream of the requested file
| name | type | required | description |
|---|---|---|---|
| fileIndex | number | v | file index in the list of document files |
| documentId | string | v | document id or did |
| serviceId | string | v | service id of array of services defining access to the asset |
| transferTxId | string | v | tx id of transaction for approval of datatokens transfer given to the provider's account |
| nonce | string | v | is required to verify a request paired with a signature |
| consumerAddress | string | v | consumer address |
| signature | string | v | signed message based on did + nonce |
| aes_encrypted_key | string | encrypted key for cipher to decrypt file |
byte array
returns log by id
{
"timestamp": "123",
"level": "123",
"message": "123",
"moduleName": "123",
"meta": "123",
}
returns list of logs
| name | type | required | description |
|---|---|---|---|
| startTime | string | filter logs from date | |
| endTime | string | filter logs to date | |
| maxLogs | string | logs per page | |
| moduleName | string | filter logs by module name | |
| level | string | filter logs by level |
[
{
"id": "123",
"timestamp": "123",
"level": "123",
"message": "123",
"moduleName": "123",
"meta": "123",
}
]
returns list of nodes providing the specific element(s) (dids, c2d resources, etc)
| name | type | required | description |
|---|---|---|---|
| input | string | v | did, c2d resource, etc |
returns list of nodes providing all specific elements.
| name | type | required | description |
|---|---|---|---|
| timeout | string | optional | timeout in miliseconds |
["{\"c2d\":{\"free\":false,\"disk\":1}}", "{\"c2d\":{\"free\":false,\"cpu\":1}}"][
{
"id": "16Uiu2HAmENNgCY1QAdQrPxipgUCQjyookUgpnbgXua4ZMju4Rkou",
"multiaddrs": [
"/ip4/10.255.255.254/tcp/41015/ws",
"/ip4/10.255.255.254/tcp/41347",
"/ip4/127.0.0.1/tcp/41015/ws",
"/ip4/127.0.0.1/tcp/41347",
"/ip4/172.27.58.101/tcp/41015/ws",
"/ip4/172.27.58.101/tcp/41347",
"/ip6/::1/tcp/37527"
]
}
]returns P2P peer
| name | type | required | description |
|---|---|---|---|
| peerId | string | v | peer id |
{
"id": "PeerId",
"addresses": [{ multiaddr: "123", isCertified: true }],
"protocols": ["123", "123", "123"],
"metadata": {},
"tags": {},
"publicKey": "0x123"
}
returns P2P peer multiaddresses if found in DHT
| name | type | required | description |
|---|---|---|---|
| peerId | string | v | peer id |
| timeout | int | optional | timeout |
{
"id": "16Uiu2HAmLhRDqfufZiQnxvQs2XHhd6hwkLSPfjAQg1gH8wgRixiP",
"multiaddrs": [
"/ip4/127.0.0.1/tcp/9000",
"/ip4/127.0.0.1/tcp/9001/ws",
"/ip4/172.18.0.2/tcp/9000",
"/ip4/172.18.0.2/tcp/9001/ws",
"/ip6/::1/tcp/9002"
]
}
returns list of all P2P peers
[
{
"id": "PeerId",
"addresses": [{ multiaddr: "123", isCertified: true }],
"protocols": ["123", "123", "123"],
"metadata": {},
"tags": {}
}
]
returns an empty object if it is valid otherwise an array with error
| name | type | required | description |
|---|---|---|---|
| command | string | v | command name |
| node | string | if not present it means current node | |
| multiAddrs | string[] | if passed, use this instead of peerStore & DHT | |
| id | string | v | document id or did |
| chainId | number | v | chain id of network on which document is provided |
| nftAddress | string | v | address of nft token |
{
"command": "validateDDO",
"node": "PeerId",
"id": "did:op:123",
"chainId": 123,
"nftAddress": "0x123"
}returns file information
| name | type | required | description |
|---|---|---|---|
| command | string | v | command name |
| node | string | if not present it means current node | |
| type | string | type of storage url or arweave or ipfs |
|
| did | string | document id or did | |
| serviceId | number | service id of services list | |
| fileIndex | number | file index in files array | |
| file | object | file data | |
| checksum | number | index in transaction events list |
{
"command": "fileInfo",
"node": "PeerId",
"type": "123",
"did": "did:op:123",
"serviceId": 123,
"fileIndex": 123,
"file": 123,
"checksum": 123
}[
{
valid: true
contentLength: "123"
contentType: "123"
name: "123"
type: "url or arweave or ipfs"
}
]
returns a message about successful addition to the reindexing queue
| name | type | required | description |
|---|---|---|---|
| command | string | v | command name |
| node | string | if not present it means current node | |
| txId | string | v | id of transaction for reindexing |
| chainId | number | v | chain id of network on which transaction is provided |
| eventIndex | number | index in transaction events list |
{
"command": "reIndex",
"node": "PeerId",
"txId": "0x123",
"chainId": 123,
"eventIndex": 123
}Added to reindex queue successfully
returns calculated provider fees for DDO with service id
| name | type | required | description |
|---|---|---|---|
| command | string | v | command name |
| node | string | if not present it means current node | |
| ddo | object | v | document object |
| serviceId | string | v | service id of services list |
{
"command": "getFees",
"node": "PeerId",
"ddo": {
"@context": ["https://w3id.org/did/v1"],
"id": "did:op:ACce67694eD2848dd683c651Dab7Af823b7dd123",
"version": "4.1.0",
"chainId": 1,
"nftAddress": "0x123",
"...": "..."
},
"serviceId": "123"
}[
{
providerFeeAddress: "0x123",
providerFeeToken: "0x123",
providerFeeAmount: 123,
providerData: "0x123",
v: 123,
r: "0x123",
s: "0x123",
validUntil: 123,
}
]
returns list of providers from which ddo can be obtained
| name | type | required | description |
|---|---|---|---|
| command | string | v | command name |
| node | string | if not present it means current node | |
| id | string | v | document id or did |
{
"command": "findDDO",
"node": "PeerId",
"id": "did:op:123"
}[
{
provider: "PeerId",
id: "did:op:123"
lastUpdateTx: "123",
lastUpdateTime: "123",
}
]
returns status of node
| name | type | required | description |
|---|---|---|---|
| command | string | v | command name |
| node | string | if not present it means current node |
{
"command": "status",
"node": "PeerId"
}{
"id": "PeerId",
"publicKey": "0x123",
"address": "0x123",
"version": "123",
"http": true,
"p2p": true,
"provider": [],
"indexer": [],
"supportedStorage": {
"ipfs": true
"arwave": true
"url": true
},
"uptime": 123,
"platform": {
"cpus": "123",
"freemem": 123,
"totalmem": 123,
"loadavg": [123],
"arch": "123",
"machine": "123",
"platform": "123",
"release": "123",
"osType": "123",
"osVersion": "123",
"node": "123"
}
}
returns search result for query
| name | type | required | description |
|---|---|---|---|
| command | string | v | command name |
| node | string | if not present it means current node | |
| query | object | v | query parameters |
| query.q | object | v | text to search for in database |
| query.query_by | object | v | one or more field names that should be queried against |
{
"command": "query",
"node": "PeerId",
"query": {
"q": "0x123",
"query_by": "nftAddress"
}
}[{
"facet_counts": [],
"found": 1,
"out_of": 1,
"page": 1,
"request_params": {
"collection_name": "ddo",
"per_page": 10,
"q": "0x123"
},
"search_time_ms": 1,
"hits": [
{
"highlights": [
{
"field": "nftAddress",
"snippet": "<mark>0x123</mark>",
"matched_tokens": ["0x123"]
}
],
"document": {
"@context": ["https://w3id.org/did/v1"],
"id": "did:op:ACce67694eD2848dd683c651Dab7Af823b7dd123",
"version": "4.1.0",
"chainId": 1,
"nftAddress": "0x123",
...
},
"text_match": 130916
}
]
}]
returns document by id
| name | type | required | description |
|---|---|---|---|
| command | string | v | command name |
| node | string | if not present it means current node | |
| id | string | v | document id or did |
{
"command": "getDDO",
"node": "PeerId",
"id": "did:op:123"
}{
"@context": ["https://w3id.org/did/v1"],
"id": "did:op:ACce67694eD2848dd683c651Dab7Af823b7dd123",
"version": "4.1.0",
"chainId": 1,
"nftAddress": "0x123",
...
}
returns encrypted blob
| name | type | required | description |
|---|---|---|---|
| command | string | v | command name |
| node | string | if not present it means current node | |
| blob | string | v | blob data |
| encoding | string | v | data encoding string or base58 |
| encryptionType | string | v | encrypt method AES or ECIES |
{
"command": "encrypt",
"node": "PeerId",
"blob": "123",
"encoding": "0x123",
"encryptionType": "0x123"
}0x123
returns stored nonce for an address
| name | type | required | description |
|---|---|---|---|
| command | string | v | command name |
| node | string | if not present it means current node | |
| address | string | v | consumer address |
{
"command": "nonce",
"node": "PeerId",
"address": "0x123"
}123
returns decrypted document
| name | type | required | description |
|---|---|---|---|
| command | string | v | command name |
| node | string | if not present it means current node | |
| decrypterAddress | string | v | decrypter address |
| chainId | number | v | chain id of network on which document is provided |
| transactionId | string | tx id of encrypted document | |
| dataNftAddress | string | address of nft token | |
| encryptedDocument | string | encrypted document | |
| flags | number | metadata flags if DDO is ECIES encrypted or lzma compressed | |
| documentHash | string | hash based on sha256 of asset | |
| nonce | string | v | is required to verify a request paired with a signature |
| signature | string | v | signed message based on transactionId + dataNftAddress + decrypterAddress + chainId + nonce |
{
"command": "decryptDDO",
"node": "PeerId",
"decrypterAddress": "0x123",
"chainId": 123,
"transactionId": "0x123",
"dataNftAddress": "0x123",
"encryptedDocument": "0x123",
"flags": 1,
"documentHash": "0x123",
"nonce": "123",
"signature": "0x123"
}{
"@context": ["https://w3id.org/did/v1"],
"id": "did:op:ACce67694eD2848dd683c651Dab7Af823b7dd123",
"version": "4.1.0",
"chainId": 1,
"nftAddress": "0x123",
...
}
returns a file stream of the requested file
| name | type | required | description |
|---|---|---|---|
| command | string | v | command name |
| node | string | if not present it means current node | |
| fileIndex | number | v | file index in the list of document files |
| documentId | string | v | document id or did |
| serviceId | string | v | service id of array of services defining access to the asset |
| transferTxId | string | v | tx id of transaction for approval of datatokens transfer given to the provider's account |
| nonce | string | v | is required to verify a request paired with a signature |
| consumerAddress | string | v | consumer address |
| signature | string | v | signed message based on did + nonce |
| aes_encrypted_key | string | encrypted key for cipher to decrypt file |
{
"command": "download",
"node": "PeerId",
"fileIndex": 0,
"documentId": "did:op:123",
"serviceId": "0",
"transferTxId": "0x123",
"nonce": "123",
"consumerAddress": "0x123",
"signature": "0x123",
"aes_encrypted_key": "0x123"
}byte array
returns the current indexing queue, as an array of objects
{
queue: []
}
Forwards request to PolicyServer (if any).
This endpoint is not authenticated. The node forwards the caller-supplied payload and adds
the resolved ddo plus its own nodeAddress. Caller-supplied identity fields are not
verified.
| name | type | required | description |
|---|---|---|---|
| command | string | v | command name |
| node | string | if not present it means current node | |
| policyServerPassthrough | object | v | command and params for PolicyServer (see docs) |
{
"policyServerPassthrough": {
"action": "newDDO",
"rawDDO": {},
"chainId": 1,
"txId": "0x123",
"eventRaw": "raw event data"
}
}{
"command": "PolicyServerPassthrough",
"node": "PeerId",
"policyServerPassthrough": {
"action": "newDDO",
"rawDDO": {},
"chainId": 1,
"txId": "0x123",
"eventRaw": "raw event data"
}
}| code | description |
|---|---|
| 200 | PolicyServer allowed the request; its response body is returned |
| 400 | missing/invalid parameters |
Asks the PolicyServer to start a verification flow (initiate action) for a given
asset/service and consumer. This endpoint requires an Authorization header or a
nonce + signature pair.
This is a distinct command from PolicyServerPassthrough, so the signed message uses its
own command string: consumerAddress + nonce + "PolicyServerInitialize".
The verified consumerAddress is the one forwarded to the PolicyServer; the caller's
authorization, nonce and signature are added to the policyServer object.
| name | type | required | description |
|---|---|---|---|
| documentId | string | v | the asset DID |
| serviceId | string | v | the service id within the asset |
| consumerAddress | string | v | the caller's address |
| policyServer | any | v | free-form data passed to the PolicyServer |
| nonce | string | required unless an Authorization token is supplied |
|
| signature | string | required unless an Authorization token is supplied |
{
"documentId": "did:op:1234",
"serviceId": "0",
"consumerAddress": "0x9876543210fedcba9876543210fedcba98765432",
"nonce": "1",
"signature": "0x123",
"policyServer": {}
}| code | description |
|---|---|
| 200 | PolicyServer response body |
| 400 | missing/invalid parameters |
| 401 | missing or invalid authentication |
| 404 | asset not found |
| 503 | DDO database not available |
returns current node configuration with sensitive data hidden (admin only)
| name | type | required | description |
|---|---|---|---|
| expiryTimestamp | number | v | expiry timestamp for the request |
| signature | string | v | signed message to authenticate admin request |
{
"expiryTimestamp": 1234567890,
"signature": "0x123"
}{
"keys": {
"privateKey": "[*** HIDDEN CONTENT ***]"
},
"chainIds": [1],
"rpcs": { "1": "https://eth-mainnet.g.alchemy.com/v2/..." },
"...": "..."
}updates node configuration and reloads it gracefully (admin only)
| name | type | required | description |
|---|---|---|---|
| expiryTimestamp | number | v | expiry timestamp for the request |
| signature | string | v | signed message to authenticate admin request |
| config | object | v | partial configuration object with fields to update |
{
"expiryTimestamp": 1234567890,
"signature": "0x123",
"config": {
"chainIds": [1],
"rpcs": { "1": "https://eth-mainnet.g.alchemy.com/v2/..." }
}
}{
"keys": {
"privateKey": "[*** HIDDEN CONTENT ***]"
},
"chainIds": [1],
"rpcs": { "1": "https://eth-mainnet.g.alchemy.com/v2/..." },
"...": "..."
}Returns indexed Escrow contract events. The indexer matches Escrow logs by topic hash, verifies they came from the chain's Escrow contract (Deposit/Withdraw/Lock are generic signatures), and stores one row per event in the append-only escrow collection keyed by ${txHash}-${logIndex}. All filters are optional.
| name | type | required | description |
|---|---|---|---|
| command | string | POST only | command name (getEscrowEvents) |
| chainId | number | chain id | |
| eventType | string | one of Auth, Lock, Claimed, Canceled, Deposit, Withdraw |
|
| payer | string | payer address (case-insensitive) | |
| payee | string | payee address (case-insensitive) | |
| token | string | token address (case-insensitive) | |
| jobId | string | compute job id | |
| txId | string | transaction hash | |
| offset | number | rows to skip (default 0) | |
| size | number | page size (default 100, max 250) |
{
"command": "getEscrowEvents",
"chainId": 8996,
"eventType": "Deposit",
"offset": 0,
"size": 50
}Every row has id, eventType, chainId, contract, block, txHash plus event-specific fields (payer, payee, token, jobId, amount, expiry, proof, maxLockedAmount, maxLockSeconds, maxLockCounts).
[
{
"id": "0x39f3...6575-3",
"eventType": "Deposit",
"chainId": 8996,
"contract": "0x282d...a1a1",
"block": 55,
"txHash": "0x39f3...6575",
"payer": "0xbe54...ab5e",
"token": "0x282d...a1a1",
"amount": "100000000000000000000"
}
]Returns a live per-node resource snapshot, rolled up across every C2D engine (the same aggregate the telemetry layer exports) blended with host os readings. Read-only, no parameters. hasAggregate is a freshness flag: when false, no engine had a fresh compute aggregate (metrics collection is disabled via C2D_METRICS_INTERVAL_SECONDS=0, or nothing has been sampled yet) and every scalar is a structural zero rather than a genuine reading. The snapshot is returned either way.
| name | type | required | description |
|---|---|---|---|
| command | string | POST only | command name (getNodeMetrics) |
{
"collectedAt": 1730370000000,
"hasAggregate": true,
"cpu": {
"usagePercent": 42.5,
"coresAllocated": 4,
"hostCores": 16,
"throttledCount": 0,
"loadAverage": [1.2, 1.1, 0.9]
},
"memory": {
"usedBytes": 2147483648,
"limitBytes": 8589934592,
"hostFreeBytes": 12000000000,
"hostTotalBytes": 34359738368
},
"disk": { "usedBytes": 1073741824 },
"network": { "rxBytes": 12345, "txBytes": 6789 },
"jobs": { "running": 1, "runningFree": 0, "queued": 0, "queuedFree": 0 },
"gpu": [
{
"resourceId": "0",
"vendor": "nvidia",
"utilizationPercent": 55,
"memoryUsedBytes": 2000000000,
"memoryTotalBytes": 16000000000,
"temperatureC": 61,
"powerWatts": 120
}
],
"env": [{ "env": "env-hash", "resource": "cpu", "total": 16, "inUse": 4 }],
"meta": { "sampledContainers": 1, "oldestSampleAgeSeconds": 8 }
}Returns ordered hourly averages of the per-node resource snapshot, persisted to SQLite by the sampler/roll-up cron jobs and retained for NODE_METRICS_RETENTION_DAYS (default 180). Scalars are arithmetic means over the hour's minute-samples; sampleCount is how many samples fed each bucket; GPU entries are averaged per resourceId, env entries per env+resource. Requires the node-metrics database (returns 503 when unavailable, e.g. history disabled via NODE_METRICS_HISTORY_ENABLED=false).
When the requested range includes the current, in-progress hour, the last bucket is a live average computed on the fly from the raw samples collected so far this hour (before the top-of-hour roll-up has stored it). It is flagged "partial": true and is the only bucket that carries that flag; every completed hour is a finalized, stored average. This lets a caller see fresh data without waiting for the hourly roll-up.
| name | type | required | description |
|---|---|---|---|
| command | string | POST only | command name (getNodeMetricsHistory) |
| startTime | number/string | range start — epoch ms or ISO-8601. Defaults to now minus the retention window | |
| stopTime | number/string | range end — epoch ms or ISO-8601. Defaults to now |
startTime must be earlier than stopTime (else 400); the range is clamped to the retention window and the row count is capped.
{
"command": "getNodeMetricsHistory",
"startTime": 1727778000000,
"stopTime": 1730370000000
}{
"startTime": 1727778000000,
"stopTime": 1730370000000,
"count": 1,
"buckets": [
{
"hourStart": 1730368800000,
"sampleCount": 60,
"cpu": { "usagePercent": 40.1, "coresAllocated": 4, "hostCores": 16, "throttledCount": 0 },
"memory": {
"usedBytes": 2000000000,
"limitBytes": 8589934592,
"hostFreeBytes": 12000000000,
"hostTotalBytes": 34359738368
},
"disk": { "usedBytes": 1073741824 },
"network": { "rxBytes": 12000, "txBytes": 6000 },
"jobs": { "running": 1, "runningFree": 0, "queued": 0, "queuedFree": 0 },
"gpu": [{ "resourceId": "0", "vendor": "nvidia", "utilizationPercent": 50 }],
"env": [{ "env": "env-hash", "resource": "cpu", "total": 16, "inUse": 4 }],
"meta": { "sampledContainers": 1 }
}
]
}For starters, you can find a list of algorithms in the Ocean Algorithms repository and the docker images in the Algo Dockerhub.
The ComputeAsset interface defines the structure of a compute asset in the Ocean Node. It can include information about the file object, document ID, service ID, transfer transaction ID, and user data.
- fileObject: Optional. An object of type
BaseFileObjectrepresenting the file associated with the compute asset. - documentId: Optional. A string representing the document ID of the compute asset.
- serviceId: Optional. A string representing the service ID associated with the compute asset.
- transferTxId: Optional. A string representing the transaction ID for the transfer of the compute asset.
- userdata: Optional. An object containing additional user-defined data related to the compute asset.
export interface ComputeAsset {
fileObject?: BaseFileObject
documentId?: string
serviceId?: string
transferTxId?: string
userdata?: { [key: string]: any }
}This interface is used to encapsulate the details of a compute asset, which can be utilized in various compute-related operations within the Ocean Node.
The ComputeAlgorithm interface defines the structure of a compute algorithm in the Ocean Node.
It can include information about the file object, document ID, service ID, transfer transaction ID, algorithm custom data, metadata and user data.
- documentId: Optional. A string representing the document ID of the compute algorithm.
- serviceId: Optional. A string representing the service ID associated with the compute algorithm.
- fileObject: Optional. An object of type
BaseFileObjectrepresenting the file associated with the compute algorithm. - meta: Optional. An object of type
MetadataAlgorithmcontaining metadata related to the compute algorithm. - transferTxId: Optional. A string representing the transaction ID for the transfer of the compute algorithm.
- algocustomdata: Optional. An object containing additional custom data related to the compute algorithm.
- userdata: Optional. An object containing additional user-defined data related to the compute algorithm.
- envs: Optional. Array of keys:values to be used as environment variables for algo.
export interface ComputeAlgorithm {
documentId?: string
serviceId?: string
fileObject?: BaseFileObject
meta?: MetadataAlgorithm
transferTxId?: string
algocustomdata?: { [key: string]: any }
userdata?: { [key: string]: any }
}This interface is used to encapsulate the details of a compute algorithm, which can be utilized in various compute-related operations within the Ocean Node.
fetch all compute environments
[
{
"id": "0x7d187e4c751367be694497ead35e2937ece3c7f3b325dcb4f7571e5972d092bd-0xf173fdc0a9c7cc1c34f8aaf6b3aafe866795851b567436e1d4fbab17b0e26ca1",
"runningJobs": 0,
"consumerAddress": "0xf9C5B7eE7708efAc6dC6Bc7d4b0455eBbf22b519",
"platform": { "architecture": "x86_64", "os": "Ubuntu 22.04.3 LTS" },
"fees": { "1": [[{ "feeToken": "0x123", "prices": [{ "id": "cpu", "price": 1 }] }]] },
"storageExpiry": 604800,
"maxJobDuration": 3600,
"minJobDuration": 60,
"minServiceDuration": 60,
"maxServiceDuration": 86400,
"resources": [
{ "id": "cpu", "total": 16, "max": 16, "min": 1, "inUse": 0 },
{
"id": "ram",
"total": 33617674240,
"max": 33617674240,
"min": 1000000000,
"inUse": 0
},
{ "id": "disk", "total": 1000000000, "max": 1000000000, "min": 0, "inUse": 0 }
],
"free": {
"maxJobDuration": 60,
"minJobDuration": 10,
"maxJobs": 3,
"resources": [
{ "id": "cpu", "max": 1, "inUse": 0 },
{ "id": "ram", "max": 1000000000, "inUse": 0 },
{ "id": "disk", "max": 1000000000, "inUse": 0 }
]
},
"runningfreeJobs": 0
}
]maxJobDuration / minJobDuration apply to compute jobs. Services have their own pair,
and SERVICE_START rejects a duration outside it:
maxServiceDuration— the ceiling. Defaults to the daemon'sserviceOnDemand.maxDurationSecondsand may be lowered per environment; a larger per-env value is clamped to the daemon ceiling at startup. SERVICE_EXTEND also caps the resulting remaining window to it.minServiceDuration— the floor. SERVICE_START rejects a shorterduration, and SERVICE_EXTEND rejects a shorteradditionalDuration: the floor is a minimum purchase, so a smaller one is refused rather than silently billed at the floor. Everything accepted is then priced by its actual duration, rounded up to whole minutes. Defaults to the environment's ownminJobDuration, and may be raised per environment; a value below the daemon'sserviceOnDemand.minDurationSecondsis clamped up at startup.
Environments on the same engine may therefore report different values for both.
Both fields are additive, and an older node omits them. Their fallbacks differ, so treat each separately:
- A missing
maxServiceDurationmeans the 86400 s (24 h) default — notmaxJobDuration, which is a different limit and is often much larger, so using it would offer windows the node rejects. - A missing
minServiceDurationmeans the environment's ownminJobDuration(raised to the daemon'sserviceOnDemand.minDurationSeconds, which itself defaults to 0 — no daemon floor). That is exactly what such a node already bills a service at.
starts a free compute job and returns jobId if succesfull
| name | type | required | description |
|---|---|---|---|
| command | string | v | command name |
| node | string | if not present it means current node | |
| consumerAddress | string | v | consumer address |
| signature | string | v | signature (msg=String(nonce) ) |
| nonce | string | v | nonce for the request |
| datasets | object | list of ComputeAsset to be used as inputs | |
| algorithm | object | ComputeAlgorithm definition | |
| environment | string | v | compute environment to use |
| resources | object | optional list of required resources | |
| metadata | object | optional metadata for the job, data provided by the user | |
| additionalViewers | object | optional array of addresses that are allowed to fetch the result | |
| queueMaxWaitTime | number | optional max time in seconds a job can wait in the queue before being started | |
| encryptedDockerRegistryAuth | string | Ecies encrypted docker auth schema for image (see Private Docker Registries with Per-Job Authentication) | |
| output | string | Ecies encrypted with instructions for uploading compute results (see C2D result upload to remote storage) | |
| outputBucketId | string | persistent-storage bucket id; the bucket is mounted at /data/outputs and results are stored there as individual files. Mutually exclusive with output (see persistent storage) |
{
"command": "freeStartCompute",
"datasets": [],
"algorithm": {
"meta": { "container": { "image": "ubuntu", "entrypoint": "/bin/bash'" } }
},
"consumerAddress": "0x00",
"signature": "123",
"nonce": 1,
"environment": "0x7d187e4c751367be694497ead35e2937ece3c7f3b325dcb4f7571e5972d092bd-0xbeaf12703d708f39ef98c3d8939ce458553254176dbb69fe83d535883c4cee38",
"resources": [{ "id": "cpu", "amount": 1 }],
"metadata": { "key": "value" }
}[
{
"owner": "0x00",
"jobId": "0x7d187e4c751367be694497ead35e2937ece3c7f3b325dcb4f7571e5972d092bd-a4ad237d-dfd8-404c-a5d6-b8fc3a1f66d3",
"dateCreated": "1742291065.119",
"dateFinished": null,
"status": 0,
"statusText": "Job started",
"results": [],
"agreementId": null,
"expireTimestamp": 1742291065.119,
"environment": "0x7d187e4c751367be694497ead35e2937ece3c7f3b325dcb4f7571e5972d092bd-0xf173fdc0a9c7cc1c34f8aaf6b3aafe866795851b567436e1d4fbab17b0e26ca1",
"resources": [
{ "id": "cpu", "amount": 1 },
{ "id": "ram", "amount": 1000000000 },
{ "id": "disk", "amount": 0 }
],
"isFree": true,
"metadata": { "key": "value" }
}
]returns job status
Required at least one of the following parameters:
| name | type | required | description |
|---|---|---|---|
| consumerAddress | string | consumer address to use as filter | |
| jobId | string | jobId address to use as filter | |
| agreementId | string | agreementId address to use as filter | |
| includeMetrics | boolean | override the runtime-metrics default (true = require them, false = never). See note below. |
|
| signature | string | signature over consumerAddress + nonce + command (or an auth token) — authenticates the owner |
|
| nonce | string | request nonce, paired with signature |
Runtime metrics are owner-only and returned BY DEFAULT. If the request carries owner credentials
(consumerAddress plus signature/nonce, or an Authorization header token), each job owned by
(or shared with) that address comes back with a runtimeMetrics object — no flag needed.
includeMetrics only overrides that default:
includeMetrics |
behavior |
|---|---|
| omitted (default) | Metrics attached when owner credentials are present and valid. A request without credentials — the plain, unauthenticated status call — returns 200 with no metrics, exactly as before. Invalid credentials likewise just mean no metrics. |
true |
Metrics are required: missing consumerAddress answers 400, failed authentication 401. Use it when you want to know why metrics are absent instead of getting a silently trimmed response. |
false |
Metrics are never attached (and the node skips the auth round-trip). |
Metrics never reach a non-owner, and are never part of the on-chain escrow claim proof. They are
best-effort and up to one sampling interval stale (see compute.md and
C2D_METRICS_INTERVAL_SECONDS in env.md).
[
{
"owner": "0x00",
"did": null,
"jobId": "a4ad237d-dfd8-404c-a5d6-b8fc3a1f66d3",
"dateCreated": "1742291065.119",
"dateFinished": null,
"status": 0,
"statusText": "Job started",
"results": [],
"inputDID": null,
"algoDID": null,
"agreementId": null,
"expireTimestamp": 1742291065.119,
"environment": "0x7d187e4c751367be694497ead35e2937ece3c7f3b325dcb4f7571e5972d092bd-0xf173fdc0a9c7cc1c34f8aaf6b3aafe866795851b567436e1d4fbab17b0e26ca1",
"resources": [
{
"id": "cpu",
"amount": 1
},
{
"id": "ram",
"amount": 1000000000
},
{
"id": "disk",
"amount": 1000000000
}
],
"isFree": true,
"metadata": { "key": "value" }
}
]When called with owner credentials, each owned job additionally carries a runtimeMetrics object
(see The runtimeMetrics object below).
runtimeMetrics is an optional snapshot of live container stats, returned on
COMPUTE_GET_STATUS / SERVICE_GET_STATUS to the authenticated owner of the job or service —
by default, without asking for it. It never reaches anyone else, and includeMetrics=false opts out.
Clients MUST treat every part as optional and render a field only when present.
Semantics clients should surface to users:
- Best-effort & slightly stale. Sampled on a fixed cadence (
C2D_METRICS_INTERVAL_SECONDS, default 10s), so values can be up to one interval old.collectedAtis the sample time — show it (e.g. "as of 8s ago"). - May be missing entirely. No snapshot yet (job just started), collection disabled on the node
(
C2D_METRICS_INTERVAL_SECONDS=0), or a transient sampling failure ⇒ noruntimeMetricsfield. This is normal, not an error. nullvs absent for GPU numbers. Inside agpu[]entry, anullmetric means "the backend could not read it" — display as "n/a", never as0.- Bytes are raw bytes; percentages are rounded to two decimals — memory/disk/GPU are
0–100, but CPUusagePercentcan exceed100across multiple cores (see the CPU table); durations are seconds; the final snapshot after a job/service ends carries the peak/exit values.
| field | type | unit / notes |
|---|---|---|
| collectedAt | string | ISO-8601 timestamp of the sample |
| containerState | object | see below — status + structured exit info |
| cpu | object | see below |
| memory | object | see below |
| disk | object | see below |
| network | object? | { rxBytes, txBytes }; absent when the container runs with no network (NetworkMode: none) |
| blockIO | object | { readBytes, writeBytes } — cumulative disk I/O in bytes |
| pids | object | { current, limit } — process/thread count vs the container PID limit (512) |
| gpu | array? | one entry per GPU the job/service holds; absent for CPU-only jobs or when GPU metrics are off |
containerState:
| field | type | notes |
|---|---|---|
| status | string | e.g. running, exited |
| startedAt | string? | ISO-8601 |
| finishedAt | string? | ISO-8601; present once the container has stopped |
| exitCode | number? | process exit code (present after exit) |
| oomKilled | boolean | true if the kernel OOM-killed the container |
| error | string? | Docker-reported error string, if any |
| restartCount | number | container restarts |
| health | string? | Docker HEALTHCHECK status when the image defines one (e.g. healthy) |
cpu:
| field | type | unit / notes |
|---|---|---|
| usagePercent | number | % of one host CPU-second per wall-second (docker-stats formula), 0–N×100 |
| allocated | number | CPU cores requested by the job (0 when unconstrained) |
| usagePercentOfAllocated | number | usagePercent / allocated — "how saturated is what you paid for" (0 if alloc 0) |
| cumulativeSeconds | number | total CPU-seconds consumed since start (monotonic; billing-grade) |
| throttledPeriods | number | CFS quota throttling events — high ⇒ the CPU request is too small |
| throttledSeconds | number | total time throttled, seconds |
memory:
| field | type | unit / notes |
|---|---|---|
| usageBytes | number | working-set bytes (usage − inactive_file, cgroup v2) |
| limitBytes | number | memory limit (= allocated RAM) in bytes |
| usagePercent | number | usageBytes / limitBytes × 100 |
| peakUsageBytes | number | max usageBytes observed across samples |
disk:
| field | type | unit / notes |
|---|---|---|
| usedBytes | number | compute jobs: bytes written under / (excludes base image); services: writable layer |
| quotaBytes | number? | present only for jobs with a disk resource |
| usagePercent | number? | present only when quotaBytes is known |
gpu[] entry:
| field | type | unit / notes |
|---|---|---|
| resourceId | string | the requested resource id (gpu0, gpu1, …) — maps the entry to a device |
| vendor | string | nvidia (only NVIDIA is emitted today; amd/intel reserved) |
| utilizationPercent | number | null | GPU busy % (null = unreadable) |
| memoryUsedBytes | number | null | VRAM used |
| memoryTotalBytes | number | null | total VRAM |
| temperatureC | number? | °C, when available |
| powerWatts | number? | current draw, W, when available |
| shared | boolean? | true ⇒ device is shareable and the number may include other jobs |
Note: the node also keeps an internal delta accumulator on the stored snapshot; it is stripped from the response and clients will never see it.
{
"collectedAt": "2026-07-29T12:00:10.000Z",
"containerState": {
"status": "running",
"startedAt": "2026-07-29T11:59:30.000Z",
"oomKilled": false,
"restartCount": 0
},
"cpu": {
"usagePercent": 182.4,
"allocated": 2,
"usagePercentOfAllocated": 91.2,
"cumulativeSeconds": 73.1,
"throttledPeriods": 12,
"throttledSeconds": 0.4
},
"memory": {
"usageBytes": 734003200,
"limitBytes": 1073741824,
"usagePercent": 68.36,
"peakUsageBytes": 812345678
},
"disk": { "usedBytes": 524288000, "quotaBytes": 10737418240, "usagePercent": 4.88 },
"network": { "rxBytes": 10485760, "txBytes": 2097152 },
"blockIO": { "readBytes": 41943040, "writeBytes": 8388608 },
"pids": { "current": 24, "limit": 512 },
"gpu": [
{
"resourceId": "gpu0",
"vendor": "nvidia",
"utilizationPercent": 77,
"memoryUsedBytes": 1073741824,
"memoryTotalBytes": 3221225472,
"temperatureC": 55,
"powerWatts": 90
}
]
}returns job result
| name | type | required | description |
|---|---|---|---|
| consumerAddress | string | v | consumer address to use as filter |
| jobId | string | v | jobId address to use as filter |
| signature | string | v | signature (consumerAddress + jobId + index.toString() + nonce) |
| nonce | string | v | nonce for the request |
| index | number | v | index of result (0 for main result, 1 for logs) |
File content
Create a new persistent storage bucket. Bucket ownership is set to the request consumerAddress.
| name | type | required | description |
|---|---|---|---|
| Authorization | string | auth token (optional; depends on node auth configuration) |
{
"consumerAddress": "0x...",
"signature": "0x...",
"nonce": "123",
"accessLists": []
}{
"bucketId": "uuid",
"owner": "0x...",
"accessList": []
}List buckets for a given owner. Results are filtered by bucket access lists for the calling consumer.
| name | type | required | description |
|---|---|---|---|
| consumerAddress | string | v | consumer address |
| signature | string | v | signed message (consumerAddress + nonce + command) |
| nonce | string | v | request nonce |
| chainId | number | v | chain id (used by auth/signature checks) |
| owner | string | v | bucket owner to filter by |
[
{
"bucketId": "uuid",
"owner": "0x...",
"createdAt": 1710000000,
"accessLists": []
}
]List files in a bucket.
| name | type | required | description |
|---|---|---|---|
| consumerAddress | string | v | consumer address |
| signature | string | v | signed message (consumerAddress + nonce + command) |
| nonce | string | v | request nonce |
[
{
"bucketId": "uuid",
"name": "hello.txt",
"size": 123,
"lastModified": 1710000000
}
]Return the fileObject for a specific file in a bucket (useful for passing references to other subsystems like compute).
| name | type | required | description |
|---|---|---|---|
| consumerAddress | string | v | consumer address |
| signature | string | v | signed message (consumerAddress + nonce + command) |
| nonce | string | v | request nonce |
{
"type": "nodePersistentStorage",
"bucketId": "uuid",
"fileName": "hello.txt"
}Download a file from a bucket. The response body is the raw file bytes. Enforces the bucket
access list (the consumer must be the bucket owner or on the bucket ACL). The same operation is
available as the persistentStorageDownloadFile P2P command, which streams the raw bytes back
identically.
| name | type | required | description |
|---|---|---|---|
| consumerAddress | string | v | consumer address |
| signature | string | v | signed message (consumerAddress + nonce + command) |
| nonce | string | v | request nonce |
Raw file bytes, sent with:
Content-Type: application/octet-streamContent-Disposition: attachment; filename="<fileName>"Content-Length(best-effort)
| status | when |
|---|---|
| 403 | consumer is not the bucket owner and not on the bucket ACL |
| 404 | file not found in the bucket |
Upload a file to a bucket. The request body is treated as raw bytes.
| name | type | required | description |
|---|---|---|---|
| consumerAddress | string | v | consumer address |
| signature | string | v | signed message (consumerAddress + nonce + command) |
| nonce | string | v | request nonce |
Raw bytes (any content-type).
{
"bucketId": "uuid",
"name": "hello.txt",
"size": 123,
"lastModified": 1710000000
}Delete a file from a bucket.
| name | type | required | description |
|---|---|---|---|
| consumerAddress | string | v | consumer address |
| signature | string | v | signed message (consumerAddress + nonce + command) |
| nonce | string | v | request nonce |
| chainId | number | v | chain id (used by auth/signature checks) |
{ "success": true }Service-on-Demand lets a consumer launch a long-running Docker container (e.g. JupyterLab, a
vLLM inference server, VS Code) on a compute environment, pay up front via on-chain escrow for
a requested duration, and reach it over forwarded network endpoints
(http://<nodeHost>:<hostPort>) while it runs. Unlike a compute job, a service stays up until
it expires, is stopped, or is extended. See services.md for the full design
and security model.
All routes live under /api/services. Every command except serviceTemplates is
authenticated by a signature over consumerAddress + nonce + command (or an auth-token
Authorization header). Cost is computed only from the environment's server-side pricing and
charged to the authenticated consumerAddress.
Note: service containers run hardened (
no-new-privileges,CapDrop: ['ALL']), so a process inside the container cannot bind to a port below 1024 — have your service listen on a high port (the published host port is allocated by the node regardless).
Operator-published blueprint. Secret envVars values are never returned — only their keys via
envVarKeys.
| property | type | description |
|---|---|---|
| id | string | template id ([a-z0-9][a-z0-9_-]{0,63}) |
| name / description | string | human-readable labels |
| image | string | base image |
| tag / checksum / dockerfile | string | image spec — exactly one |
| exposedPorts | number[] | container ports to forward |
| envVarKeys | string[] | keys of operator-set env vars (values never returned) |
| userConfigurableEnvVars | object[] | { key, validation?, sensitive? } passed via userData |
| command / entrypoint | string[] | Docker CMD / ENTRYPOINT overrides |
| requiredResources | object[] | resources the service MUST have to run |
| recommendedResources | object[] | resources for best performance |
The encrypted userData is never returned. Key fields:
| property | type | description |
|---|---|---|
| serviceId | string | unique id of the running service |
| environment | string | envId the service runs on |
| owner | string | consumerAddress |
| status | number | 10 Starting, 20 Locking, 11 PullImage, 13 BuildImage, 30 Claiming, 40 Running, 12 PullImageFailed, 14 BuildImageFailed, 15 VulnerableImage, 50 Stopping, 70 Stopped, 75 Expired, 99 Error |
| statusText | string | human-readable status |
| dateCreated | string | ISO timestamp |
| expiresAt | number | Unix ms timestamp when the paid window ends |
| duration | number | requested seconds |
| endpoints | object[] | { containerPort, hostPort, url } per exposed port |
| resources | object[] | { id, amount, price } |
| payment | object | initial start payment record |
| extendPayments | object[] | one entry per successful extend |
List the operator-published service templates (sanitized). Not authenticated.
| name | type | required | description |
|---|---|---|---|
| chainId | number | filter to templates whose envs price on this chain |
[
{
"id": "jupyter-cpu",
"name": "JupyterLab (CPU)",
"image": "quay.io/jupyter/datascience-notebook",
"tag": "latest",
"exposedPorts": [8888],
"userConfigurableEnvVars": [{ "key": "JUPYTER_TOKEN", "sensitive": true }],
"requiredResources": [
{ "id": "cpu", "min": 1 },
{ "id": "ram", "min": 2 }
]
}
]Validate the request, persist the job, and return immediately with the serviceId — the
response does not wait for escrow or the image pull/build. The consumer supplies the
container spec directly (an image referenced by tag/checksum, or an inline dockerfile
when the operator allows building).
The returned job has status: 10 (Starting) and no endpoints yet. A background loop then
advances it: Starting → Locking (escrow lock) → PullImage/BuildImage (image + scan) → Claiming (claim on success, or refund/cancel the lock on failure) → Running. Poll
serviceStatus until status is 40 (Running, with endpoints populated) or a terminal
*Failed / Error status. Note that Running is not a final resting state for a poller to stop
at: the same background loop keeps checking the container's health afterward, and can move an
already-Running service to Error later if the container dies on its own — long-lived clients
should keep watching serviceStatus, not just stop once they first see Running.
{
"consumerAddress": "0x...",
"nonce": "123",
"signature": "0x...",
"environment": "env-1",
"image": "nginxinc/nginx-unprivileged",
"tag": "alpine",
"exposedPorts": [8080],
"resources": [
{ "id": "cpu", "amount": 1 },
{ "id": "ram", "amount": 1 }
],
"duration": 3600,
"userData": "<ECIES-encrypted-to-node-pubkey hex>",
"metadata": { "run": "experiment-7", "attempt": 2 },
"payment": { "chainId": 8996, "token": "0x..." }
}| field | type | required | description |
|---|---|---|---|
| environment | string | v | envId to run on (services must be enabled on it) |
| image | string | v | base image |
| tag / checksum / dockerfile | string | image spec — at most one; dockerfile requires allowImageBuild |
|
| additionalDockerFiles | object | filename → content; only with dockerfile |
|
| dockerCmd / dockerEntrypoint | string[] | container CMD / ENTRYPOINT overrides | |
| exposedPorts | number[] | container ports to publish | |
| resources | object[] | { id, amount } requested resources |
|
| duration | number | v | seconds; capped by serviceOnDemand.maxDurationSeconds |
| userData | string | ECIES-encrypted (to the node pubkey) JSON of env vars | |
| metadata | object | arbitrary user labels (string/number/boolean values, ≤1 KB JSON); node-opaque. Returned on both serviceStatus and serviceList |
|
| payment | object | v | { chainId, token } |
The immediate response — Starting, no endpoints yet. Poll serviceStatus for the rest.
[
{
"serviceId": "0x...",
"environment": "env-1",
"owner": "0x...",
"status": 10,
"statusText": "Starting",
"expiresAt": 1735689600000,
"duration": 3600,
"endpoints": [],
"payment": { "chainId": 8996, "token": "0x...", "cost": 10 }
}
]Errors: 403 services disabled on the env / access denied, 400 invalid params (bad address,
duration, image spec, metadata over 1 KB, unavailable resources, or no pricing for the token).
Escrow lock/claim now
happens in the background, so escrow failures surface as the job ending in an Error / *Failed
status (observed via serviceStatus), not as a synchronous 402.
Read service job status and endpoints. Authenticated and owner-scoped — only services owned
by the authenticated consumerAddress are returned.
| name | type | required | description |
|---|---|---|---|
| consumerAddress | string | v | owner address |
| nonce | string | v | request nonce |
| signature | string | v | signed message (or use an Authorization auth-token header) |
| serviceId | string | filter to a single service; omit to list all owned services | |
| includeMetrics | boolean | runtime metrics (runtimeMetrics) are included by default; pass false to omit them |
Array of ServiceJob (with userData stripped; any user-supplied metadata is kept — this
path is owner-scoped). Each entry also carries a sanitized
runtimeMetrics object — see The runtimeMetrics object for its full
structure. Included by default here because this command is already authenticated and owner-scoped
(pass includeMetrics=false to omit); the node-wide serviceList never returns metrics. Metrics are
best-effort (see compute.md and C2D_METRICS_INTERVAL_SECONDS in env.md).
Node-wide service listing. Authenticated but NOT owner-scoped — any authenticated
consumer identity sees every owner's services. By default it returns only the services
currently holding a resource reservation (exactly what the engines count against the
shared pools): Running/Restarting/Stopping, the mid-start pipeline states, paid
Error (container died, restartable), and explicitly Stopped within the paid window.
Expired and never-paid jobs hold nothing and are not listed by default.
| name | type | required | description |
|---|---|---|---|
| consumerAddress | string | v | caller identity (any consumer) |
| nonce | string | v | request nonce |
| signature | string | v | signed message (or use an Authorization auth-token header) |
| status | number | filter to ONE specific ServiceStatusNumber (any status, incl. 75 Expired); takes precedence over includeAllStatuses |
|
| includeAllStatuses | boolean | true returns services in every status instead of only the resource-holding set |
|
| fromTimestamp | string | only services created at/after this moment — ISO date (2026-01-15T00:00:00Z) or Unix timestamp (seconds or milliseconds) |
Array of ServiceJob, listing-sanitized: userData, dockerCmd, dockerEntrypoint,
dockerfile and additionalDockerFiles are stripped (identity, status, resources,
endpoints, payment metadata and the owner's metadata are kept). Use the owner-scoped
serviceStatus to see a service's own configuration.
Pay to push the service expiry further out. The total remaining duration must not exceed
maxDurationSeconds. Re-checks the environment access list.
{
"consumerAddress": "0x...",
"nonce": "123",
"signature": "0x...",
"serviceId": "0x...",
"additionalDuration": 1800,
"payment": { "chainId": 8996, "token": "0x..." }
}additionalDuration must be a positive number of seconds.
The updated ServiceJob (advanced expiresAt, new entry in extendPayments).
Recreate the service container (no extra charge), keeping the same expiresAt, resources and
host ports. Re-checks the environment service gate and access list; rejected if the service has
expired. This is the recommended recovery path after a service lands in Error because its
container died on its own (the background health check leaves host ports/network/container
record reserved specifically so restart can reuse them), in addition to recovering from an
explicit serviceStop or any other terminal failure.
Restart is atomic — either all-old or all-new, never a mix of new params over the stored job:
- REUSE mode — the request carries none of the container params below. The service
restarts on exactly its stored spec (image,
userData,dockerCmd,dockerEntrypoint). Use this to simply bounce a service back toRunning. - RESPEC mode — the request carries any container param. The container is rebuilt
entirely from the request:
imagebecomes required and exactly one oftag/checksum/dockerfileapplies (validated exactly likeserviceStart).userData/dockerCmd/dockerEntrypointare taken as-sent — anything omitted here is empty/unset, it is not pulled from the stored job. This is the bug-fix flow: publish a fixed image under a new tag, then restart re-supplying the full spec (e.g. sameimage, newtag).
Because image is mandatory whenever any container param is present, you cannot ride a new
userData/dockerCmd on top of the stored image — a partial change is rejected (400). Payment,
resources and duration are always preserved; only the container spec can change. A service whose
start payment was never claimed (escrow lock failed or was refunded) cannot be restarted —
start a new service instead.
REUSE mode (bounce the service on its stored spec):
{
"consumerAddress": "0x...",
"nonce": "123",
"signature": "0x...",
"serviceId": "0x..."
}RESPEC mode (restart on a new image spec — image required, plus at most one of
tag/checksum/dockerfile):
{
"consumerAddress": "0x...",
"nonce": "123",
"signature": "0x...",
"serviceId": "0x...",
"image": "myrepo/myservice",
"tag": "v2",
"userData": "<optional ECIES-encrypted hex; the new container env — omitted ⇒ none>",
"dockerCmd": ["<optional; the new CMD override — omitted ⇒ none>"],
"dockerEntrypoint": ["<optional; the new ENTRYPOINT override — omitted ⇒ none>"]
}| name | type | required | description |
|---|---|---|---|
| serviceId | string | v | the service to restart |
| image | string | RESPEC | base image name (build label when dockerfile is set). Required as soon as any container param is present |
| tag | string | pull by name:tag; mutually exclusive with checksum/dockerfile |
|
| checksum | string | pull by digest sha256:<64 hex>; mutually exclusive with tag/dockerfile |
|
| dockerfile | string | build from an inline Dockerfile; requires allowImageBuild on the environment; mutually exclusive with tag/checksum |
|
| additionalDockerFiles | object | extra filename → content files for the build context (only with dockerfile) |
|
| userData | string | ECIES-encrypted (to the node public key) JSON → the container's env-var map | |
| dockerCmd | string[] | exact container command (Docker exec-form CMD override) | |
| dockerEntrypoint | string[] | container ENTRYPOINT override | |
| metadata | object | user labels (≤1 KB JSON). Not a container param — independent of REUSE/RESPEC. When present it replaces the stored metadata; when omitted the original metadata is kept |
The ServiceJob with a new containerId (same hostPort and expiresAt; the image/tag/
checksum/dockerfile/containerImage fields reflect the new spec in RESPEC mode).
Not found, expired, payment never claimed, metadata over 1 KB, or an invalid respec — a
container param was sent without image, or more than one of tag/checksum/dockerfile was
provided.
dockerfile was supplied but the environment has allowImageBuild=false.
Tear down the service container and network. Owner-gated. The paid reservation is kept until
expiresAt; optional release: true ends the paid window now so the expiry sweep frees it
instead — no refund, no restart.
{
"consumerAddress": "0x...",
"nonce": "123",
"signature": "0x...",
"serviceId": "0x...",
"release": false
}The ServiceJob with status: 70 (Stopped).
Stream the service container's stdout/stderr logs live. Authenticated and owner-scoped
— only the service's owner (consumerAddress, proven by signature/nonce or auth token) can
read its logs. Available while the service is Running (40) or Error (99) — a crashed
container is kept around until stop/restart, so its logs remain fetchable for diagnosis.
| name | type | required | description |
|---|---|---|---|
| consumerAddress | string | v | owner address |
| nonce | string | v | request nonce |
| signature | string | v | signed message (or use an Authorization auth-token header) |
| serviceId | string | v | the service to stream logs for |
| since | string | lower time bound for returned logs. Either a Unix timestamp in seconds (e.g. 1735689600), or a relative duration counted back from now (e.g. 30s, 45m, 2h, 7d). Omit to get the full history since container start, then follow live. |
Raw stdout/stderr byte stream from the container, connection kept open and followed live.
With since set, historical output before that point is skipped — useful for a long-lived
service where fetching the full history would otherwise dump days/weeks of buffered logs
before reaching the live tail (e.g. since=1h for just the last hour).
since is present but not a valid Unix timestamp or duration (<number><s|m|h|d>).
Service not found, or not Running/Error.
Missing/invalid auth, or consumerAddress is not the service owner.