# WebSocket API

For this guide we'll be using JavaScript and the `ws` library to handle the WebSocket connection.

## Connect to SEDA Fast

Once you have your API key you can open a WebSocket connection to SEDA Fast. After establishing the connection, the server sends an authorized message if the API key is valid. You must wait for this message before sending execution or feed requests.

**Example code:**

```js
const WebSocket = require("ws");

const ws = new WebSocket("wss://fast-ws.mainnet.seda.xyz/ws/v1", {
	headers: { Authorization: `Bearer ${process.env.SEDA_FAST_API_KEY}` },
});

ws.on("open", () => console.log("🔌 Connected"));

ws.on("message", (data) => {
	const msg = JSON.parse(data.toString());

	// Uncomment this line to see the full message
	// console.log("📨", JSON.stringify(msg, null, 2));

	if (msg.method === "authorized") {
		console.log("✅ Authorized");

		// Close the connection after receiving the authorized notification
		ws.close(1000, "closed by client");
	}

	if (msg.error) {
		console.error("❌", msg.error.code, msg.error.message);
	}
});

ws.on("error", (e) => console.error("❌", e));

ws.on("close", (code, reason) => {
	console.log("🔌 Disconnected", code ? `(${code}${reason ? `: ${reason}` : ""})` : "");
});
```

### Executing a Single Request

Once the authorisation works it is time to execute an Oracle Program.&#x20;

```js
const WebSocket = require("ws");

const executeRequest = {
	jsonrpc: "2.0",
	// For this example we're using a hardcoded request ID
	id: "req-1",
	// The method, either "feed.execute", "feed.start", or "feed.stop"
	method: "feed.execute",
	// The parameters of the request. These are the same as the execute endpoint on the REST API
	params: {
		execProgramId: "0x568732e496819819f2effa240614b8ebe53c2492149443578e155a145e0a351e",
		execInputs: "0x9b190a0000000000",
		inputEncoding: "auto",
		includeDebugInfo: "true",
		injectLastResult: "success",
		encoding: "json",
	},
};

const ws = new WebSocket(WS_URL, {
	headers: { Authorization: `Bearer ${process.env.SEDA_FAST_API_KEY}` },
});

ws.on("open", () => console.log("🔌 Connected"));

ws.on("message", (data) => {
	const msg = JSON.parse(data.toString());

	// Uncomment this line to see the full message
	// console.log("📨", JSON.stringify(msg, null, 2));

	if (msg.method === "authorized") {
		console.log("✅ Authorized");
		// When authorized immediately send an execute request
		ws.send(JSON.stringify(executeRequest));
	}

	// We check for the same ID that we specified in the executeRequest
	if (msg.id === "req-1" && msg.result !== undefined) {
		const exitCode = msg.result.data.dataResult.exitCode;
		console.log("📊 Result:", !exitCode ? "success" : "failed");

		// Close the connection after receiving the result
		ws.close(1000, "closed by client");
	}

	if (msg.error) {
		console.error("❌", msg.error.code, msg.error.message);
	}
});

ws.on("error", (e) => console.error("❌", e));

ws.on("close", (code, reason) => {
	console.log("🔌 Disconnected", code ? `(${code}${reason ? `: ${reason}` : ""})` : "");
});
```

### Understanding the Response

A successful response includes:

```json
{
	"jsonrpc": "2.0",
	"id": "req-1",
	"result": {
		"_tag": "ExecuteResponse",
		"data": {
			"id": "603f6ded-fd32-4881-92af-893bfd73b39d",
			"requestId": "603f6ded-fd32-4881-92af-893bfd73b39d",
			"dataRequest": {
				"consensusFilter": "00",
				"execGasLimit": "300000000000000",
				"execInputs": "9b190a0000000000",
				"execProgramId": "568732e496819819f2effa240614b8ebe53c2492149443578e155a145e0a351e",
				"gasPrice": "0",
				"memo": "",
				"replicationFactor": 1,
				"tallyGasLimit": "50000000000000",
				"tallyInputs": "",
				"tallyProgramId": "568732e496819819f2effa240614b8ebe53c2492149443578e155a145e0a351e",
				"version": "0.0.1"
			},
			"dataResult": {
				"drId": "7f3f862bc15fc136f68df81533b852b01cf533745968f28ea84bdde5bb95c404",
				"gasUsed": "30208555168125",
				"blockHeight": "0",
				"blockTimestamp": "1772030619437",
				"consensus": true,
				"exitCode": 0,
				"version": "0.0.1",
				"result": "0000000000000000000000000003f4dc",
				"paybackAddress": "",
				"sedaPayload": ""
			},
			"signature": "90a1b3827d8c828f8508655aea09dbc6c6c600280e1481fa2698eaaf0e802a182f17ba83418c2133726a51836736956f6fa5d18b5d2287548082951ec967aa9d01",
			"result": "0000000000000000000000000003f4dc",
			"execute": {
				"result": {
					"value": 259292
				},
				"gasUsed": "14244011134375",
				"stdout": "Received input: 661915\nRandom output: 259292\n",
				"stderr": "",
				"exitCode": 0
			},
			"tally": {
				"result": "0000000000000000000000000003f4dc",
				"gasUsed": "15964544033750",
				"stdout": "50\nReveal 0: 259292\n",
				"stderr": "",
				"exitCode": 0
			}
		}
	}
}
```

**Key fields:**

* `id` - The JSON-rpc id of the request.
* `result.data.dataRequest` - Details of the Oracle Program execution request (program IDs, inputs, gas limits)
* `result.data.dataResult.result` - The hex-encoded final result from the Oracle Program
* `result.data.dataResult.exitCode` - 0 indicates success, non-zero indicates an error
* `result.data.dataResult.gasUsed` - Total gas consumed during execution
* `result.data.dataResult.drId` - Unique identifier for this data request
* `result.data.signature` - Cryptographic signature for verification
* `result.data.result` - Convenient top-level access to the result (same as dataResult.result)

{% hint style="info" %}
Additional fields may be included in the response depending on the parameters used. The WebSocket API supports the same parameters as the REST API. See the [REST API Reference](/home/for-developers/define-your-delivery-method/seda-fast/api-reference/rest-api.md) for complete details on all available parameters and response fields.
{% endhint %}

### Failed Execution Error Handling

In the event that the message is valid and the request succeeds, but the execution of the Oracle Program fails, the WebSocket service sends an error response for the JSON-RPC request id with a "FailedExecutionResponse" tag.

**Example failed execution response**

```json
{
	"jsonrpc": "2.0",
	"id": "req-1",
	"error": {
		"_tag": "FailedExecutionResponse",
		"data": {
			"id": "134d5b2e-1d8b-4eba-a5d4-e0164f542d9f",
			"requestId": "134d5b2e-1d8b-4eba-a5d4-e0164f542d9f",
			"dataRequest": {
				"consensusFilter": "00",
				"execGasLimit": "300000000000000",
				"execInputs": "9b190a0000000000",
				"execProgramId": "051e798acc909ec8c3b7d28e6d3c92b3f71be374e68df22859da1bf9edb39f9b",
				"gasPrice": "0",
				"memo": "",
				"replicationFactor": 1,
				"tallyGasLimit": "50000000000000",
				"tallyInputs": "",
				"tallyProgramId": "051e798acc909ec8c3b7d28e6d3c92b3f71be374e68df22859da1bf9edb39f9b",
				"version": "0.0.1"
			},
			"dataResult": {
				"drId": "dc320315532dc277adb4699559a3de7f018efe7f73322560c50e3801f78c3a96",
				"gasUsed": "10011213748750",
				"blockHeight": "0",
				"blockTimestamp": "1772033409794",
				"consensus": false,
				"exitCode": 254,
				"version": "0.0.1",
				"result": "",
				"paybackAddress": "",
				"sedaPayload": ""
			},
			"signature": "25c0a156309db7a3eafe8bd051a85aa6ed1a71cf256591fcbdda7327a9c4bdbe37e6a1564027264299b369f94ed368106af8dc1559f51dadfd9a6d14cb4b4d7a01",
			"result": "",
			"execute": {
				"result": "Execution phase error: expected value at line 1 column 1",
				"gasUsed": "10011213748750",
				"stdout": "",
				"stderr": "Execution phase error: expected value at line 1 column 1\n",
				"exitCode": 1
			},
			"tally": {
				"result": "",
				"gasUsed": "0",
				"stdout": "",
				"stderr": "",
				"exitCode": 254
			}
		}
	}
}
```

When debugging failures, the response can include additional fields like "stderr" and "stdout" to help identify the issue (use the `includeDebugInfo` parameter to enable these).

### WebSocket Error Handling

Any other errors, such as invalid parameters or invalid JSON have a smaller error response with a code and the error message.

**Example**

```json
{
    "jsonrpc": "2.0",
    "id": "req-1",
    "error": {
        "code": -32000,
        "message": "Program 051e798acc909ec8c3b7d28e6d3c92b3f71be374e68df22859da1bf9edb39f9a not found"
    }
}
```

For a complete list of error codes and their meanings, see the Error Codes section in the [WebSocket API Reference](/home/for-developers/define-your-delivery-method/seda-fast/api-reference/websocket-api.md).

## Managing a Feed

To receive continuous updates you can start a feed. A feed continues running and sending `feed.result` messages until:

* The client sends `feed.stop` for the feed
* The WebSocket connection is closed

It is similar to executing a single request:

```js
const WebSocket = require("ws");

const feedStartRequest = {
	jsonrpc: "2.0",
	// For this example we're using a hardcoded request ID
	id: "req-1",
	// The method, either "feed.execute", "feed.start", or "feed.stop"
	method: "feed.start",
	params: {
	  // The parameters of the request. These are the same as the execute endpoint on the REST API
	  execute: {
			execProgramId: "0x568732e496819819f2effa240614b8ebe53c2492149443578e155a145e0a351e",
			execInputs: "0x9b190a0000000000",
			inputEncoding: "auto",
			includeDebugInfo: "true",
			injectLastResult: "success",
			encoding: "json",
		},
		// The update frequency of the feed in milliseconds
		periodicityMs: 5000,
		// The scheduling mode, see the API reference for more details
		mode: "FIXED",
	},
};

const ws = new WebSocket(WS_URL, {
	headers: { Authorization: `Bearer ${process.env.SEDA_FAST_API_KEY}` },
});

const applicationState = {
	/** Null when there is no feed, otherwise a string */
	feedId: null,
	/** Number of reports received */
	resultsReceived: 0,
};

// Stop the feed after receiving 2 updates
const STOP_AFTER_FEED_RESULTS = 2;

ws.on("open", () => console.log("🔌 Connected"));

ws.on("message", (data) => {
	const msg = JSON.parse(data.toString());

	// Uncomment this line to see the full message
	// console.log("📨", JSON.stringify(msg, null, 2));

	if (msg.method === "authorized") {
		console.log("✅ Authorized");
		// When authorized immediately start the feed
		ws.send(JSON.stringify(feedStartRequest));
	}

	// We check for the same ID that we specified in the executeRequest and store the feed id
	if (msg.id === "req-1" && msg.result !== undefined && msg.result.feedId) {
		applicationState.feedId = msg.result.feedId;
		console.log("✅ Feed started:", applicationState.feedId);
	}

	if (msg.method === "feed.result" && msg.params?.feedId === applicationState.feedId) {
		applicationState.resultsReceived += 1;
		console.log(`📬 feed.result ${applicationState.resultsReceived}/${STOP_AFTER_FEED_RESULTS}`);

		if (applicationState.resultsReceived >= STOP_AFTER_FEED_RESULTS) {
			console.log("📤 Sending feed.stop...");

			ws.send(
				JSON.stringify({
					jsonrpc: "2.0",
					id: "req-2",
					method: "feed.stop",
					params: { feedId: applicationState.feedId },
				}),
			);
		}
	}

	// We check for the same ID that we specified in the feed.stop message
	if (msg.id === "req-2" && msg.result !== undefined) {
		console.log("✅ Feed stopped");

		// Close the connection after receiving the response for the feed.stop request
		ws.close(1000, "closed by client");
	}

	if (msg.error) {
		console.error("❌", msg.error.code, msg.error.message);
	}
});

ws.on("error", (e) => console.error("❌", e));

ws.on("close", (code, reason) => {
	console.log("🔌 Disconnected", code ? `(${code}${reason ? `: ${reason}` : ""})` : "");
});
```

### Understanding a Feed Result

A successful response is very similar to the `feed.execute` response, just wrapped in another object that contains the feed id for this result:

```json
{
  "jsonrpc": "2.0",
  "method": "feed.result",
  "params": {
    "feedId": "233028d0-6140-4fa5-ab54-9ca465891eb1",
    "result": {
       "_tag": "ExecuteResponse",
       "data": {
         // etc...
       }
    }
  }
}
```

### Feed Error Handling

In the event that the execution of the Oracle Program in the feed fails, the WebSocket service sends an error response for the JSON-rpc request id. This is similar to `feed.execute`, just wrapped in another object that contains the feed id for this failed execution.

```json
{
  "jsonrpc": "2.0",
  "method": "feed.result",
  "params": {
    "feedId": "f8d8ec75-24c7-4daf-b136-462081d34d24",
    "error": {
      "_tag": "FailedExecutionResponse",
      "data": {
        "id": "f0f367b0-c666-4357-9ec2-cf20a21d90d3",
        "requestId": "f0f367b0-c666-4357-9ec2-cf20a21d90d3",
        // etc..
      }
    }
  }
}
```


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://docs.seda.xyz/home/for-developers/define-your-delivery-method/seda-fast/getting-started/websocket-api.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
