Skip to main content
This guide covers WebSocket ingestion for managed RisingWave Cloud projects, where connections are routed through the managed RWProxy ingest endpoint.
WebSocket ingestion for managed RisingWave Cloud is currently in preview. If you’d like to enable it, contact us at cloud-support@risingwave-labs.com and we’ll help you get set up.
If you are running self-hosted RisingWave (Operator/Helm/bare metal), use Ingest data from webhook. The same webhook table can also accept WebSocket ingest at ws://<frontend-host>:4560/ingest/<database>/<schema>/<table>.

How managed WebSocket routing works

In managed RisingWave Cloud, WebSocket ingest connections are accepted by RWProxy over WSS and forwarded to your project’s frontend ingest listener.
  • Public ingress: WSS :443
  • Ingest path format: /ingest/<database>/<schema>/<table>
Tenant routing:
  1. Hosted projects: SNI hostname first, then tenant query parameter fallback.
  2. BYOC projects: currently use the tenant query parameter (SNI is not supported).

Prerequisites

  1. A running RisingWave Cloud project.
  2. The tenant identifier of your project (for example, rwc-g1huxxxxxx-mycluster).
  3. A webhook table in your target database/schema.
For details on tenant identifier and project connection details, see Connection errors.
Current BYOC limitations:
  • SNI is not supported yet. Use ?tenant=<tenant identifier> in the ingest URL.
  • TLS certificate verification may fail unless verification is disabled on the client side. For testing, temporarily disable TLS verification in your WebSocket client if needed.

Hosted vs BYOC requirements

Project typeTenant routing requirementTLS note
HostedUse wss://<cloud-host>/ingest/<database>/<schema>/<table>. Hosted projects support SNI routing, with query-parameter fallback.Use standard TLS verification.
BYOCUse wss://<cloud-host>/ingest/<database>/<schema>/<table>?tenant=<tenant identifier>. BYOC currently does not support SNI routing.TLS verification can fail in some BYOC environments. For testing, disable verification in your client temporarily.
Use the wizard as the primary setup path so you do not need to manually construct hostnames, endpoint URLs, or signature validation SQL. Wizard steps:
  1. Open your project workspace and go to Data Catalog.
  2. Click Create source.
  3. Open WebSocket ingest.
  4. Select Database, Schema, and Target table.
  5. If you do not have a target table yet, click Create table, enter the table name and signature settings, review the generated SQL, and create the table.
  6. Copy the generated Endpoint.
  7. Expand Wire format and Client demo to copy the generated init frame, DML batch examples, and client template.
Wizard screenshots: WebSocket ingest entry in Data Catalog
WebSocket ingest card in the Data Catalog create source dialog in RisingWave Cloud
Create a WebSocket ingest target and review generated SQL
Create WebSocket ingest target dialog showing generated SQL preview
Generated endpoint and client examples
RisingWave Cloud WebSocket ingest wizard showing the generated endpoint, wire format examples, and client demo
If WebSocket ingest does not appear in your Cloud Portal yet, use the manual endpoint format below as a fallback.

Manual endpoint format

In hosted projects, use the following URL format:
wss://<cloud-host>/ingest/<database>/<schema>/<table>
For BYOC projects, append the tenant identifier:
wss://<cloud-host>/ingest/<database>/<schema>/<table>?tenant=<tenant identifier>

Reference SQL and client example

Use these as reference commands. In practice, prefer the wizard-generated SQL, endpoint, and client template. Create a WebSocket ingest target table Create a webhook table with signature validation. The WebSocket connection reuses the same validation logic as the HTTP webhook endpoint.
CREATE TABLE ws_orders (
  id int,
  customer_name varchar,
  amount double precision,
  primary key (id)
) WITH (
  connector = 'webhook'
) VALIDATE AS secure_compare(
  headers->>'x-rw-signature',
  'sha256=' || encode(hmac('TEST_WEBSOCKET', payload, 'sha256'), 'hex')
);

Protocol reference

Use the following protocol after connecting to the WebSocket endpoint:
  • Handshake header: x-rw-signature: sha256=<hmac_of_init_message>
  • First frame: {"type":"init","timestamp":<epoch_ms>}
  • DML batch frame: {"dml_batch_id":<u64>,"items":[{"op":"upsert|insert|update|delete","data":{...}}]}
  • Server ack: {"ack":<dml_batch_id>}
  • Fatal error: {"fatal":"<reason>"}
Rules and behavior:
  • The init frame must be the first text frame.
  • timestamp must be a non-negative epoch millisecond within the configured clock-skew window.
  • dml_batch_id must increase monotonically within a connection.
  • Empty batches are valid and are acknowledged immediately.
  • Non-empty batches are acknowledged after the batch is accepted downstream.
  • Any fatal error closes the connection.
  • insert and update use the same upsert-style row handling as upsert.
Supported JSON decoder option headers on the handshake:
  • x-rw-webhook-json-timestamp-handling-mode
  • x-rw-webhook-json-timestamptz-handling-mode
  • x-rw-webhook-json-time-handling-mode
  • x-rw-webhook-json-bigint-unsigned-handling-mode
  • x-rw-webhook-json-handle-toast-columns

Python client example

This example computes the HMAC of the init frame, opens the WebSocket connection, sends one batch, and waits for the ack.
import asyncio
import hashlib
import hmac
import json
import time

import websockets

SECRET = b"TEST_WEBSOCKET"
INIT_MSG = json.dumps(
    {"type": "init", "timestamp": int(time.time() * 1000)},
    separators=(",", ":"),
)
SIGNATURE = "sha256=" + hmac.new(SECRET, INIT_MSG.encode(), hashlib.sha256).hexdigest()

URL = "wss://<cloud-host>/ingest/<database>/<schema>/<table>"


async def main():
    async with websockets.connect(
        URL,
        additional_headers={"x-rw-signature": SIGNATURE},
    ) as ws:
        await ws.send(INIT_MSG)
        await ws.send(
            json.dumps(
                {
                    "dml_batch_id": 1,
                    "items": [
                        {
                            "op": "upsert",
                            "data": {
                                "id": 1,
                                "customer_name": "Alice",
                                "amount": 99.99,
                            },
                        }
                    ],
                },
                separators=(",", ":"),
            )
        )
        print(await ws.recv())


asyncio.run(main())
For BYOC projects that require the tenant query parameter, use the BYOC URL format shown above. If TLS verification fails in your BYOC environment, create a client SSL context that disables certificate verification for testing only.

HTTP webhook vs WebSocket ingest

OptionBest forTransportDelivery pattern
HTTP webhookPer-event pushes from SaaS webhook providersHTTPS POSTOne request per event
WebSocket ingestLow-latency app-driven streaming and batched DMLLong-lived WSS connectionAsync batch acknowledgements

Troubleshooting

  • Invalid signature: recompute x-rw-signature from the exact init frame bytes.
  • Stale or skewed init timestamp: regenerate the init frame immediately before connecting.
  • Non-monotonic dml_batch_id: increase the batch ID for every batch sent on the same connection.
  • Missing primary key fields: include all PK columns for upsert, insert, update, and delete.
  • JSON decode failures: verify field names, value types, and any x-rw-webhook-json-* decoder headers.