execute call gives you everything needed to settle a value on-chain: the value, the
aggregated signature (s, rx, ryParity), the set of signers, and the on-chain commitment address.
The gateway is stateless with respect to results — every
execute call triggers a fresh
oracle round. There is no caching layer at the gateway; freshness is guaranteed by a per-request
timestamp.Base URL
/v1/...).
Quick start
Discover the node set
Call
GET /v1/nodes to learn which oracle nodes exist and their signing keys.Read the job config
Call
GET /v1/jobs/{jobId}/config to learn how many signatures the
job requires.Sign and execute
Build an
authSig over the job ID and a current timestamp, then call
POST /v1/jobs/{jobId}/execute to get the signed result.Authentication
The execute endpoint authenticates the caller against the on-chain job owner. You prove ownership by signing a message with the job owner’s ed25519 key (a standard Solana keypair). The message to sign is:jobId_bytes— the 32 raw bytes of the job ID.timestamp— the sameuint64Unix-seconds value you put in the request body, big-endian.
authSig. The gateway recovers
the job owner from Solana and rejects the request if the signature does not match.
Endpoints
GET /health
Liveness probe. Returns200 OK when the service is up. No request parameters.
| Status | Meaning |
|---|---|
200 | Service healthy |
503 | Service degraded |
GET /v1/nodes
Returns the configured oracle node set, including each node’s libp2p address and the compressed secp256k1 signing key used for end-to-end encryption.Always
"ok".Response
GET /v1/jobs//config
Fetches the on-chain selection parameters for a job.32-byte job ID, hex-encoded (64 hex chars). A leading
0x is accepted and stripped.Always
"ok".Number of node signatures required to form a valid aggregated signature for this job.
Extra nodes selected beyond
signaturesRequired to tolerate failures. Group size =
signaturesRequired + redundancyBuffer.Fixed-point decimals the job’s value is scaled to.
Response
| Status | error | Cause |
|---|---|---|
400 | invalid job ID | jobId is not 32-byte hex |
404 | job not found | No job account exists on-chain |
500 | failed to fetch redundancy buffer | Upstream Solana read failed |
POST /v1/jobs//execute
Triggers an oracle round and returns the signed, aggregated result. This is the core endpoint.32-byte job ID, hex-encoded (64 hex chars). A leading
0x is accepted and stripped.Request body
Unix time in seconds. Must satisfy
|now - timestamp| ≤ maxAge. Also part of the signed
authSig message. With maxAge: 0 only the exact current second is accepted.Node-registry version the round should run against. Folded into the deterministic round ID.
Allowed clock skew, in seconds, between
timestamp and the gateway’s clock.ed25519 signature (64 bytes, hex;
0x accepted) over
keccak256(jobId_bytes || uint64_be(timestamp)). Verified against the on-chain job owner. See
Authentication.Describes the data source the nodes should fetch.
Optional end-to-end encryption envelope. Omit or send
null for plaintext API configs. When
present, the gateway forwards each node only its own envelope entry. See
End-to-end encryption.Response
Always
"completed" on success.Response
Errors
All errors use the envelope{ "error": "<message>" }.
| Status | error (examples) | Cause |
|---|---|---|
400 | jobId is required | Empty path parameter |
400 | malformed request body | Body is not valid JSON |
400 | timestamp: is required | timestamp missing or zero |
400 | timestamp: must be within the allowed time range | Clock skew exceeds maxAge |
400 | jobId: must be 32-byte hex | Bad job ID format |
400 | authSig: must be 64 bytes, got N | Signature wrong length |
400 | authSig: signature does not match job owner | Signature does not verify against the on-chain owner |
400 | apiConfig.url: scheme must be http or https | Invalid source URL |
400 | apiConfig.method: must be a valid HTTP method | Unsupported method |
503 | upstream timeout | No aggregated signature within node.agg_wait_seconds |
503 | (node-supplied message) | A node reported an error for the round |
500 | internal server error | Unexpected dispatch/build failure |
End-to-end encryption
When an API config contains secrets (API keys, tokens), you can encrypt it so that only the nodes selected for the round can read it — the gateway never sees the plaintext. The flow:- Locally compute which nodes will be selected for
(jobId, registryVersion, timestamp)using the deterministic selection algorithm. - Resolve secret placeholders (e.g.
{{secret.appid}}) in the API config. - Encrypt the resolved config with a fresh symmetric key, then wrap that symmetric key for each
selected node via ECDH against its
signingKey. - Send the result as
encKeyBundle, keyed by each node’sindex.
encrypt option handles all of this. The envelopes map must contain exactly the
selected node indices, since selection is timestamp-dependent.
Using the SDK
The TypeScript SDK wraps node discovery, job config, deterministic selection, encryption, auth signing, and retries.authSig and the
node selection.
Protocol internals (reference)
You don’t need these to use the API, but they explain how a round works under the hood.- Transport. The gateway sends each selected node a binary
RoundRequestover a libp2p stream on protocol/molpha/1.0.0. - Aggregation. Nodes publish the aggregated signature as JSON on the GossipSub topic
molpha/agg-sig/v1. The gateway matches the message by the deterministic round ID. - Deterministic round ID. Computed as
keccak256( keccak256("MOLPHA_PULL_ROUND_V1") || jobId || uint32_be(registryVersion) || uint64_be(timestamp) ), so the same inputs always identify the same round. - Round ID ≠ randomness. Because the round ID is deterministic in
timestamp, freshness and selection both rotate purely by changing the timestamp.