Skip to content

Connect to the WebSocket

Photon pushes transaction status updates over a WebSocket connection. This page documents the wire protocol; for a walkthrough of setting one up, see Register a WebSocket.

Endpoint

wss://photon.example.com/ws

The exact connection URL, along with your token, is returned by Register WebSocket credential.

Authentication

Header Value
x-ws-token The WebSocket token issued by Register WebSocket credential.

The token is validated before the connection upgrades. A missing or unknown token is rejected with HTTP 401 and error code AUTH_001 in the standard error envelope — no WebSocket connection is established.

Warning

Browsers cannot set custom headers on WebSocket connections, so connect from a server-side environment where you can send x-ws-token.

Message flow

  1. Greeting — immediately after the upgrade, the server sends exactly:
{"message":"WebSocket connected!"}
  1. Notifications — as your transactions change status, the server sends bare notification payloads (see the schema below). There is no event or type wrapper around them.

  2. Echo — any text message you send is echoed back as:

{"echo":"<your message>"}

This doubles as an application-level heartbeat. WebSocket ping frames are answered with pong frames.

You receive notifications for transactions submitted by the account the token belongs to. There is no subscription protocol — connecting is subscribing.

Notification payload

Notifications are sent as a single JSON object per message:

{
  "txId": "68fa3450539a3c9d28bbca33",
  "chainId": 8453,
  "status": "EXECUTED",
  "costUSD": 0.84,
  "totalNativeTokenUsed": "31500000000000",
  "gasPrice": "1500000000",
  "txHash": "0x3f8b2c…",
  "submittedAt": "2026-07-29T09:14:01.120Z",
  "updatedAt": "2026-07-29T09:14:03.512Z",
  "retries": 0
}
Field Type Presence Description
txId string always Transaction ID returned when you submitted the transaction.
chainId number always Chain the transaction targets.
status string always One of NOT_PICKED_UP, PENDING, EXECUTED, FAILED, NEEDS_TO_BE_RETRIED.
costUSD number when known Sponsored gas cost in USD.
totalNativeTokenUsed string when known Amount of native token spent, as an integer string.
gasPrice string when known Gas price used, as an integer string.
txHash string when known On-chain transaction hash.
submittedAt string always ISO-8601 timestamp of submission.
updatedAt string always ISO-8601 timestamp of this status change.
retries number always Retry attempts made so far.

Optional fields are omitted when not yet known — they are never sent as null. This payload is identical to the webhook request body sent to URLs registered via Register webhooks.

Example

wscat -c wss://photon.example.com/ws -H "x-ws-token: YOUR_WS_TOKEN"

Expected output on connect:

< {"message":"WebSocket connected!"}

Followed by notifications as your transactions progress:

< {"txId":"68fa3450539a3c9d28bbca33","chainId":8453,"status":"PENDING","submittedAt":"2026-07-29T09:14:01.120Z","updatedAt":"2026-07-29T09:14:01.120Z","retries":0}
< {"txId":"68fa3450539a3c9d28bbca33","chainId":8453,"status":"EXECUTED","costUSD":0.84,"totalNativeTokenUsed":"31500000000000","gasPrice":"1500000000","txHash":"0x3f8b2c…","submittedAt":"2026-07-29T09:14:01.120Z","updatedAt":"2026-07-29T09:14:03.512Z","retries":0}
import WebSocket from "ws";

const ws = new WebSocket("wss://photon.example.com/ws", {
  headers: { "x-ws-token": process.env.WS_TOKEN! },
});

ws.on("message", (raw) => {
  const msg = JSON.parse(raw.toString());
  if (msg.message === "WebSocket connected!") return; // greeting
  if (msg.echo !== undefined) return;                 // echo of a message you sent
  // Anything else is a transaction notification
  console.log(msg.txId, msg.status, msg.txHash ?? "(no hash yet)");
});

Delivery and reconnection

  • Updates emitted while you are disconnected are not queued or replayed on reconnect.
  • The per-connection delivery buffer is bounded; a consumer that reads too slowly can miss intermediate updates and will keep receiving from the live stream.
  • After a disconnect, reconnect with exponential backoff and reconcile any transactions still in flight via Get transaction status — the record there is authoritative.
  • Treat the greeting message as confirmation that authentication succeeded and the stream is live.